6. 関数の属性を意識するところ
● あるあるな状況(というか他に無い?)
– 関数インデックスを作成する時
test=# create index t1_idx_ok on t1 (upper(c2));
CREATE INDEX
test=# create index t1_idx_ng on t1 (to_timestamp(c2, 'YYYY-MM-DD'));
ERROR: functions in index expression must be marked IMMUTABLE
7. [おまけ]なぜ IMMUTABLEではなくSTABLE?
● to_char()などはIMMUTABLEでも良さそうなのですが?
– 外部要因で出力結果が左右されることがあるためです
– 例えばTimezoneやlocaleなど
postgres=# set lc_time TO 'ja_JP';
SET
postgres=# select to_char(now(), 'TMDay');
to_char
---------
土曜日
(1 row)
postgres=# set lc_time TO 'de_DE';
SET
postgres=# select to_char(now(), 'TMDay');
to_char
---------
Samstag
(1 row)
9. 例) 定数呼び出し時の差
CREATE OR REPLACE FUNCTION get_val_im(i int) RETURNS int AS
$$
DECLARE
ret int;
BEGIN
SELECT val INTO ret FROM my_val WHERE c1 = i;
RETURN ret;
END;
$$
LANGUAGE plpgsql IMMUTABLE;
上記と同じ内容をSTABLE、VOLATILEの属性指定で作成
10. 例) 定数呼び出し時の差
postgres=# explain analyze select get_val_im(1) FROM
generate_series(1,100000);
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------
Function Scan on generate_series (cost=0.00..10.00 rows=1000 width=4) (actual time=19.542..38.856 rows=100000 loops=1)
Planning time: 1.109 ms
Execution time: 53.288 ms
(3 rows)
postgres=# explain analyze select get_val_st(1) FROM generate_series(1,100000);
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------
Function Scan on generate_series (cost=0.00..260.00 rows=1000 width=4) (actual time=19.855..1130.991 rows=100000 loops=1)
Planning time: 0.044 ms
Execution time: 1150.938 ms
(3 rows)
postgres=# explain analyze select get_val_vo(1) FROM generate_series(1,100000);
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------
Function Scan on generate_series (cost=0.00..260.00 rows=1000 width=4) (actual time=20.324..1194.374 rows=100000 loops=1)
Planning time: 0.043 ms
Execution time: 1212.163 ms
(3 rows)
11. 例) 定数呼び出し時の差
postgres=# create table tbl (c1 int primary key, c2 text);
postgres=# insert into tbl select generate_series(1,20000) , 'D';
postgres=# explain analyze select * FROM tbl WHERE c1 = get_val_im(1);
QUERY PLAN
--------------------------------------------------------------------------------------------------------------
Index Scan using tbl_pkey on tbl (cost=0.29..8.30 rows=1 width=6) (actual time=0.020..0.022
rows=1 loops=1)
Index Cond: (c1 = 100)
Planning time: 0.245 ms
Execution time: 0.045 ms
(4 rows)
postgres=# explain analyze select * FROM tbl WHERE c1 = get_val_st(1);
QUERY PLAN
--------------------------------------------------------------------------------------------------------------
Index Scan using tbl_pkey on tbl (cost=0.54..8.55 rows=1 width=6) (actual time=0.055..0.057
rows=1 loops=1)
Index Cond: (c1 = get_val_st(1))
Planning time: 0.208 ms
Execution time: 0.085 ms
(4 rows)
postgres=# explain analyze select * FROM tbl WHERE c1 = get_val_vo(1);
QUERY PLAN
--------------------------------------------------------------------------------------------------
Seq Scan on tbl (cost=0.00..5339.00 rows=1 width=6) (actual time=2.790..242.450 rows=1
loops=1)
Filter: (c1 = get_val_vo(1))
Rows Removed by Filter: 19999
Planning time: 0.092 ms
Execution time: 242.477 ms
(5 rows)