У меня есть таблица с некоторыми text
и некоторыми numeric
столбцами, например:
dimension_1, dimension_2, counter_1, counter_2
, и вместо выполнения запроса
SELECT dimension_1, dimension_2, (counter_1, NULLIF(counter_2, 0)) as kpi
from table order by kpi desc nulls last;
Я хочу создатьfunction and do:
SELECT dimension_1, dimension_2, func(counter_1, counter_2) as kpi
from table order by kpi desc nulls last;
Я использовал следующую реализацию в Postgres:
CREATE FUNCTION kpi_latency_ext_msec(val1 numeric, val2 numeric)
RETURNS numeric AS $func$
BEGIN
RETURN ($1 / NULLIF($2, 0::numeric));
END; $func$
LANGUAGE PLPGSQL SECURITY DEFINER IMMUTABLE;
и получил желаемый результат, но с более низкой производительностью.
От EXPLAIN ANALYZE
Я получаю:
1-й запрос (с функцией):
Sort (cost=800.85..806.75 rows=2358 width=26) (actual time=5.534..5.710 rows=2358 loops=1)
Sort Key: (kpi_latency_ext_msec(external_tcp_handshake_latency_sum, external_tcp_handshake_latency_samples))
Sort Method: quicksort Memory: 281kB
-> Seq Scan on counters_by_cgi_rat (cost=0.00..668.76 rows=2358 width=26) (actual time=0.142..4.233 rows=2358 loops=1)
Filter: (("timestamp" >= '2018-05-10 00:00:00'::timestamp without time zone) AND ("timestamp" < '2018-05-13 00:00:00'::timestamp without time zone) AND (granularity = '1 day'::interval))
Planning time: 0.221 ms
Execution time: 5.881 ms
2-й запрос (без функции):
Sort (cost=223.14..229.04 rows=2358 width=26) (actual time=1.933..2.114 rows=2358 loops=1)
Sort Key: ((external_tcp_handshake_latency_sum / NULLIF(external_tcp_handshake_latency_samples, 0::numeric)))
Sort Method: quicksort Memory: 281kB
-> Seq Scan on counters_by_cgi_rat (cost=0.00..91.06 rows=2358 width=26) (actual time=0.010..1.190 rows=2358 loops=1)
Filter: (("timestamp" >= '2018-05-10 00:00:00'::timestamp without time zone) AND ("timestamp" < '2018-05-13 00:00:00'::timestamp without time zone) AND (granularity = '1 day'::interval))
Planning time: 0.139 ms
Execution time: 2.279 ms
Выполнение запросов без ORDER BY
:
Без функции:
Seq Scan on table (cost=0.00..91.06 rows=2358 width=26) (actual time=0.016..1.223 rows=2358 loops=1)
С функцией:
Seq Scan on table (cost=0.00..668.76 rows=2358 width=26) (actual time=0.123..3.518 rows=2358 loops=1)
РЕЗУЛЬТАТЫ для функции без ОПРЕДЕЛИТЕЛЯ БЕЗОПАСНОСТИ
Seq Scan on counters_by_cgi_rat (cost=0.00..668.76 rows=2358 width=26)
(actual time=0.035..3.718 rows=2358 loops=1)
Filter: (("timestamp" >= '2018-05-10 00:00:00'::timestamp without time zone)
AND ("timestamp" < '2018-05-13 00:00:00'::timestamp without time zone)
AND (granularity = '1 day'::interval))
Planning time: 0.086 ms
Execution time: 3.923 ms
РЕЗУЛЬТАТЫ для простого запроса
Seq Scan on counters_by_cgi_rat (cost=0.00..91.06 rows=2358 width=26)
(actual time=0.017..1.175 rows=2358 loops=1)
Filter: (("timestamp" >= '2018-05-10 00:00:00'::timestamp without time zone)
AND ("timestamp" < '2018-05-13 00:00:00'::timestamp without time zone)
AND (granularity = '1 day'::interval))
Planning time: 0.105 ms
Execution time: 1.356 ms
РЕЗУЛЬТАТЫ с языком SQL
Seq Scan on counters_by_cgi_rat (cost=0.00..91.06 rows=2358 width=26)
(actual time=0.011..1.123 rows=2358 loops=1)
Filter: (("timestamp" >= '2018-05-10 00:00:00'::timestamp without time zone)
AND ("timestamp" < '2018-05-13 00:00:00'::timestamp without time zone)
AND (granularity = '1 day'::interval))
Planning time: 0.180 ms
Execution time: 1.294 ms
БЫСТРО достаточно с языком SQL
Конечно, это быстрее, чем с использованием языка plpgsql но немного медленнее, чем исходный запрос (после повторных запусков)
========= ОБНОВЛЕНИЕ =========
CREATE FUNCTION kpi_latency_ext_msec(val1 numeric, val2 numeric)
RETURNS numeric LANGUAGE sql STABLE AS
'SELECT $1 / NULLIF($2, 0)';
Наилучшие результаты, полученные с помощью вышеуказанной функции (даже быстрее, чем простой запрос)