CREATE LANGUAGE plpgsql;
SELECT * FROM pg_language WHERE lanname = 'plpgsql'; --確認
以降はスーパーユーザーの必要は無いので、データベースオーナーが実施。
テスト用テーブルの作成。
CREATE TABLE public.tb( id serial NOT NULL, note text, create_time timestamp without time zone NOT NULL DEFAULT now(), CONSTRAINT tb_pkey PRIMARY KEY (id) );
ログ保存用スキーマlogsを作成して、そこにログ用テーブルを追加。
CREATE SCHEMA logs;
CREATE TABLE logs.log_tb AS SELECT 0::bigint AS log_id,''::text AS oper,null::timestamp without time zone AS copy_time,* FROM public.tb WHERE id = 0; CREATE SEQUENCE logs.log_tb_log_id_seq; ALTER TABLE logs.log_tb ADD PRIMARY KEY (log_id);
トリガ関数を定義。
CREATE OR REPLACE FUNCTION public.tb_copy() RETURNS trigger AS $BODY$ BEGIN IF (TG_OP = 'DELETE') THEN INSERT INTO logs.log_tb SELECT nextval('logs.log_tb_log_id_seq'::regclass),TG_OP,now(),* FROM public.tb WHERE id = OLD.id; RETURN OLD; ELSE INSERT INTO logs.log_tb SELECT nextval('logs.log_tb_log_id_seq'::regclass),TG_OP,now(),* FROM public.tb WHERE id = NEW.id; RETURN NEW; END IF; END; $BODY$ LANGUAGE plpgsql VOLATILE;
トリガをtbに仕掛ける。
CREATE TRIGGER tb_change AFTER INSERT OR UPDATE ON public.tb FOR EACH ROW EXECUTE PROCEDURE public.tb_copy(); CREATE TRIGGER tb_delete BEFORE DELETE ON public.tb FOR EACH ROW EXECUTE PROCEDURE public.tb_copy();
最後に、データを操作してログが取れるかを確認。
INSERT INTO public.tb(note) VALUES('TEST'); INSERT INTO public.tb(note) VALUES('TEST2'); UPDATE public.tb SET note = 'UPDATE TEST'; DELETE FROM public.tb; SELECT * FROM logs.log_tb;
WITH RECURSIVE r AS( SELECT p1,p2,distance ,1 as n , array[p1, p2] AS route FROM pair2 UNION SELECT r.p1, p.p2, r.distance+p.distance as distance,n+1 , route||array[p.p2] FROM pair2 p JOIN r ON p.p1 = r.p2 -- 関連key AND r.p1 != p.p2 -- 同じ値は不要 AND NOT(p.p2 = ANY(route)) -- 一度通った場所は通らない WHERE n+1 < 10 -- 継続回数を指定 ) SELECT * FROM r WHERE p1 = 'A' ORDER BY p1,p2,n,distance;
p1 p2 distance n route A B 8 1 {A,B} A D 74 2 {A,B,D} A E 152 3 {A,B,D,E} A F 196 4 {A,B,D,E,F} A G 232 5 {A,B,D,E,F,G} A H 234 6 {A,B,D,E,F,G,H} A I 257 7 {A,B,D,E,F,G,H,I} A J 328 7 {A,B,D,E,F,G,H,J}
psql [database] -f /usr/share/pgsql/contrib/textsearch_ja.sql psql [database] CREATE INDEX test_tb_content ON test_tb USING gin(to_tsvector('japanese', content));
実行結果
EXPLAIN SELECT * FROM test_tb WHERE to_tsvector('japanese', content) @@ to_tsquery('japanese', '猫'); QUERY PLAN ---------------------------------------------------------------------------------------- Bitmap Heap Scan on test_tb (cost=19.65..1470.66 rows=471 width=529) Recheck Cond: (to_tsvector('japanese'::regconfig, content) @@ '''猫'''::tsquery) -> Bitmap Index Scan on test_tb_content (cost=0.00..19.53 rows=471 width=0) Index Cond: (to_tsvector('japanese'::regconfig, content) @@ '''猫'''::tsquery) (4 行)
tar xzf pg_bigm-1.2-20161011.tar.gz cd pg_bigm-1.2-20161011/ make USE_PGXS=1 make USE_PGXS=1 install
スーパーユーザーでデータベースごとにextensionを追加します。
su - postgres psql [database] CREATE EXTENSION pg_bigm;
インデックスを追加して実行してみます。
CREATE INDEX test_tb_content_bigm ON test_tb USING gin (content gin_bigm_ops); EXPLAIN SELECT * FROM test_tb WHERE content LIKE '%猫%'; QUERY PLAN --------------------------------------------------------------------------------------- Bitmap Heap Scan on test_tb (cost=40.06..71.29 rows=8 width=529) Recheck Cond: (content ~~ '%猫%'::text) -> Bitmap Index Scan on test_tb_content_bigm (cost=0.00..40.06 rows=8 width=0) Index Cond: (content ~~ '%猫%'::text) (4 行)
$ docker run -it --rm --link postgres96beta4:postgres postgres:9.6 psql -h postgres -U postgres Password for user postgres: psql (9.6beta4) Type "help" for help.
postgres=# select version(); version --------------------------------------------------------------------------------------------- PostgreSQL 9.6beta4 on x86_64-pc-linux-gnu, compiled by gcc (Debian 4.9.2-10) 4.9.2, 64-bit (1 row)
$ psql -h localhost -p 15432 -U postgres Password for user postgres: psql (9.3.5, server 9.6beta4) WARNING: psql major version 9.3, server major version 9.6. Some psql features might not work. Type "help" for help.
$ docker exec -it postgres96beta4 bash root@d2e991bd61a7:/# cd /var/lib/postgresql/data/ root@d2e991bd61a7:/var/lib/postgresql/data# more postgresql.conf と、このようにpostgresql.confを眺めてみたり(lessもviもなかった)。
root@d2e991bd61a7:/var/lib/postgresql/data# more pg_hba.conf pg_hba.confを眺めてみたり。pg_hba.confから抜粋しますと
# TYPE DATABASE USER ADDRESS METHOD # "local" is for Unix domain socket connections only local all all trust # IPv4 local connections: host all all 127.0.0.1/32 trust # IPv6 local connections: host all all ::1/128 trust
host all all 0.0.0.0/0 md5 ローカルからはtrustで他はパスワード認証ですかね。
RUN localedef -i ja_JP -c -f UTF-8 -A /usr/share/locale/locale.alias ja_JP.UTF-8 ENV LANG ja_JP.utf8 日本語化されたイメージ、mypostgres:9.6を作ります。
$ docker build -t mypostgres:9.6 ./ 確認
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE mypostgres 9.6 4b7ea9f892dd 24 seconds ago 269.2 MB データベースの初期化操作しているディレクトリに/docker-entrypoint-initdb.d対応用にinitdbというディレクトリを作成しました。postgres/9.6/docker-entrypoint.sh を読んでみたところ
for f in /docker-entrypoint-initdb.d/*; do case "$f" in *.sh) echo "$0: running $f"; . "$f" ;; *.sql) echo "$0: running $f"; "${psql[@]}" < "$f"; echo ;; *.sql.gz) echo "$0: running $f"; gunzip -c "$f" | "${psql[@]}"; echo ;; *) echo "$0: ignoring $f" ;; esac echo done で順次処理となっているのでファイル名順に実行されるようです。sqlをgzip圧縮したsql.gz拡張子のファイルにも対応しているようですね。
初期化時にdockerユーザーとdockerテーブルを作成するshを作成します。
initdb/01adduser.sh
#!/bin/bash set -e
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" <<-EOSQL CREATE USER docker; ALTER USER docker WITH PASSWORD 'dockerpassword'; CREATE DATABASE docker; GRANT ALL PRIVILEGES ON DATABASE docker TO docker; EOSQL 次にsqlファイル内で\cでデータベースとユーザーをdockerに切り替えてテーブルを作ってみます。shでdb接続からしたほうが簡単な気がしますが、実験です。
initdb/02createtb.sql
\c docker docker
CREATE TABLE auth_group ( id integer NOT NULL, name character varying(80) NOT NULL );
COPY auth_group (id, name) FROM stdin; 1 Gyoumu 2 Master 3 System \. 日本語化したmypostgres:9.6からMacのinitdbディレクトリを/docker-entrypoint-initdb.dにバインドしてコンテナを起動します。
SELECT haisou_daityou.syouhin_detail_id, syouhin_detail.code as syouhin_detail_code, haisou_daityou.haisou_bi, date_part('day'::text, haisou_daityou.haisou_bi)::int as day, sum(haisou_daityou.haisou_suu) AS haisou_sum
FROM haisou_daityou LEFT OUTER JOIN syouhin_detail ON haisou_daityou.syouhin_detail_id = syouhin_detail.id
WHERE "YearMonth"(haisou_daityou.haisou_bi) = "YearMonth"('2016-03-01'::date)
GROUP BY haisou_daityou.syouhin_detail_id, syouhin_detail.code, haisou_daityou.haisou_bi
ORDER BY syouhin_detail.code, haisou_daityou.haisou_bi
結果
GroupAggregate (cost=1141834.38..1173281.22 rows=1048228 width=18) (actual time=30736.728..35349.575 rows=9393 loops=1) -> Sort (cost=1141834.38..1144454.95 rows=1048228 width=18) (actual time=30735.447..34864.067 rows=1024085 loops=1) Sort Key: syouhin_detail.code, haisou_daityou.haisou_bi, haisou_daityou.syouhin_detail_id Sort Method: external merge Disk: 30048kB -> Hash Left Join (cost=650914.54..994020.09 rows=1048228 width=18) (actual time=1812.137..5063.999 rows=1024085 loops=1) Hash Cond: (haisou_daityou.syouhin_detail_id = syouhin_detail.id) -> Bitmap Heap Scan on haisou_daityou (cost=650901.04..979593.45 rows=1048228 width=12) (actual time=1811.261..4238.580 rows=1024085 loops=1) Recheck Cond: ((((date_part('year'::text, (haisou_bi)::timestamp without time zone))::integer * 100) + (date_part('month'::text, (haisou_bi)::timestamp without time zone))::integer) = 201603) Rows Removed by Index Recheck: 191609 -> Bitmap Index Scan on haisou_daityou_tokuisaki_yearmonth_updated (cost=0.00..650638.98 rows=1048228 width=0) (actual time=1808.060..1808.060 rows=1024085 loops=1) Index Cond: ((((date_part('year'::text, (haisou_bi)::timestamp without time zone))::integer * 100) + (date_part('month'::text, (haisou_bi)::timestamp without time zone))::integer) = 201603) -> Hash (cost=8.78..8.78 rows=378 width=10) (actual time=0.847..0.847 rows=379 loops=1) Buckets: 1024 Batches: 1 Memory Usage: 17kB -> Seq Scan on syouhin_detail (cost=0.00..8.78 rows=378 width=10) (actual time=0.007..0.697 rows=379 loops=1) Total runtime: 35426.470 ms
うわぁ。おーそーいー。開発環境なので本番と比べると非常に非力ではありますが…
特に
「Hash LEFT JOIN (cost=650914.54..994020.09 ROWS=1048228 width=18) (actual TIME=1812.137..5063.999 ROWS=1024085 loops=1)」
SELECT syouhin_detail.code as syouhin_detail_code, sum_tmp.* FROM ( SELECT haisou_daityou.syouhin_detail_id, haisou_daityou.haisou_bi, date_part('day'::text, haisou_daityou.haisou_bi)::int as day, sum(haisou_daityou.haisou_suu) AS haisou_sum
FROM haisou_daityou
WHERE haisou_daityou.haisou_bi BETWEEN '2016-03-01'::date AND '2016-03-31'::date
GROUP BY haisou_daityou.syouhin_detail_id, haisou_daityou.haisou_bi ) as sum_tmp
LEFT OUTER JOIN syouhin_detail ON sum_tmp.syouhin_detail_id = syouhin_detail.id