Функция, реализованная в C
, на существенно * на 1003 * быстрее, чем все, что мы можем достичь с помощью LANGUAGE sql
или plpgsql
.Таким образом, ваша plpythonu
функция выигрывает в конкурсе на результат.
Но plpythonu
- это ненадежный процедурный язык.По умолчанию он не установлен, и только суперпользователи могут создавать функции на ненадежных языках.Вы должны знать о последствиях безопасности.А ненадежные языки вообще недоступны в большинстве облачных сервисов.
Текущее руководство (цитата из стр. 10):
PL / Python доступно только как «ненадежный », то есть он не предлагает каких-либо ограничений на то, что пользователи могут делать на нем, и поэтому называется plpythonu
.Доверенный вариант plpython
может стать доступным в будущем, если в Python будет разработан механизм безопасного выполнения.Создатель функции в ненадежном PL / Python должен позаботиться о том, чтобы эту функцию нельзя было использовать для нежелательных действий, поскольку она сможет делать все, что может сделать пользователь, вошедший в систему в качестве администратора базы данных.Только суперпользователи могут создавать функции на ненадежных языках, таких как plpythonu
.
Тестированные вами функции SQL плохо оптимизированы.Существует тысяча и один способов повысить производительность , но:
Демо
-- func to create random strings
CREATE OR REPLACE FUNCTION f_random_string(int)
RETURNS text AS
$func$
SELECT array_to_string(ARRAY(
SELECT substr('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz', (ceil(random()*62))::int, 1)
FROM generate_series(1, $1)
), '')
$func$ LANGUAGE sql VOLATILE;
-- test tbl with 100K rows
CREATE TABLE tbl(str text);
INSERT INTO tbl
SELECT f_random_string(15)
FROM generate_series(1, 100000) g;
VACUUM ANALYZE tbl;
-- 1: your test function 1 (inefficient)
CREATE OR REPLACE FUNCTION sort1(text) RETURNS text AS
$func$ -- your test function 1 (very inefficient)
SELECT string_agg(c, '')
FROM (SELECT unnest(regexp_split_to_array($1, '')) AS c ORDER BY c) t;
$func$ LANGUAGE sql IMMUTABLE;
-- 2: your test function 2 ( inefficient)
CREATE OR REPLACE FUNCTION sort2(text) RETURNS text AS
$func$
WITH t(s) AS (VALUES ($1))
SELECT string_agg(substr(t.s, g.g, 1), '' ORDER BY substr(t.s, g.g, 1))
FROM t
CROSS JOIN LATERAL generate_series(1, length(t.s)) g;
$func$ LANGUAGE sql IMMUTABLE;
-- 3: remove pointless CTE from sort2
CREATE OR REPLACE FUNCTION sort3(text) RETURNS text AS
$func$
SELECT string_agg(substr($1, g, 1), '' ORDER BY substr($1, g, 1))
FROM generate_series(1, length($1)) g;
$func$ LANGUAGE sql IMMUTABLE;
-- 4: use unnest instead of calling substr N times
CREATE OR REPLACE FUNCTION sort4(text) RETURNS text AS
$func$
SELECT string_agg(c, '' ORDER BY c)
FROM unnest(string_to_array($1, NULL)) c
$func$ LANGUAGE sql IMMUTABLE;
-- 5: ORDER BY in subquery
CREATE OR REPLACE FUNCTION sort5(text) RETURNS text AS
$func$
SELECT string_agg(c, '')
FROM (
SELECT c
FROM unnest(string_to_array($1, NULL)) c
ORDER BY c
) sub
$func$ LANGUAGE sql IMMUTABLE;
-- 6: SRF in SELECT list
CREATE OR REPLACE FUNCTION sort6(text) RETURNS text AS
$func$
SELECT string_agg(c, '')
FROM (SELECT unnest(string_to_array($1, NULL)) c ORDER BY 1) sub
$func$ LANGUAGE sql IMMUTABLE;
-- 7: ARRAY constructor instead of aggregate func
CREATE OR REPLACE FUNCTION sort7(text) RETURNS text AS
$func$
SELECT array_to_string(ARRAY(SELECT unnest(string_to_array($1, NULL)) c ORDER BY c), '')
$func$ LANGUAGE sql IMMUTABLE;
-- 8: The same with COLLATE "C"
CREATE OR REPLACE FUNCTION sort8(text) RETURNS text AS
$func$
SELECT array_to_string(ARRAY(SELECT unnest(string_to_array($1 COLLATE "C", NULL)) c ORDER BY c), '')
$func$ LANGUAGE sql IMMUTABLE;
SELECT str, sort1(str), sort2(str), sort3(str), sort4(str), sort5(str), sort6(str), sort7(str), sort8(str) FROM tbl LIMIT 1; -- result sample
str | sort1 | sort2 | sort3 | sort4 | sort5 | sort6 | sort7 | sort8
:-------------- | :-------------- | :-------------- | :-------------- | :-------------- | :-------------- | :-------------- | :-------------- | :--------------
tUkmori4D1rHhI1 | 114DhHiIkmorrtU | 114DhHiIkmorrtU | 114DhHiIkmorrtU | 114DhHiIkmorrtU | 114DhHiIkmorrtU | 114DhHiIkmorrtU | 114DhHiIkmorrtU | 114DHIUhikmorrt
EXPLAIN (ANALYZE, TIMING OFF) SELECT sort1(str) FROM tbl;
| QUERY PLAN |
| :--------------------------------------------------------------------------------------- |
| Seq Scan on tbl (cost=0.00..26541.00 rows=100000 width=32) (actual rows=100000 loops=1) |
| Planning time: 0.053 ms |
| Execution time: 2742.904 ms |
EXPLAIN (ANALYZE, TIMING OFF) SELECT sort2(str) FROM tbl;
| QUERY PLAN |
| :--------------------------------------------------------------------------------------- |
| Seq Scan on tbl (cost=0.00..26541.00 rows=100000 width=32) (actual rows=100000 loops=1) |
| Planning time: 0.105 ms |
| Execution time: 2579.397 ms |
EXPLAIN (ANALYZE, TIMING OFF) SELECT sort3(str) FROM tbl;
| QUERY PLAN |
| :--------------------------------------------------------------------------------------- |
| Seq Scan on tbl (cost=0.00..26541.00 rows=100000 width=32) (actual rows=100000 loops=1) |
| Planning time: 0.079 ms |
| Execution time: 2191.228 ms |
EXPLAIN (ANALYZE, TIMING OFF) SELECT sort4(str) FROM tbl;
| QUERY PLAN |
| :--------------------------------------------------------------------------------------- |
| Seq Scan on tbl (cost=0.00..26541.00 rows=100000 width=32) (actual rows=100000 loops=1) |
| Planning time: 0.075 ms |
| Execution time: 2194.780 ms |
EXPLAIN (ANALYZE, TIMING OFF) SELECT sort5(str) FROM tbl;
| QUERY PLAN |
| :--------------------------------------------------------------------------------------- |
| Seq Scan on tbl (cost=0.00..26541.00 rows=100000 width=32) (actual rows=100000 loops=1) |
| Planning time: 0.083 ms |
| Execution time: 1902.829 ms |
EXPLAIN (ANALYZE, TIMING OFF) SELECT sort6(str) FROM tbl;
| QUERY PLAN |
| :--------------------------------------------------------------------------------------- |
| Seq Scan on tbl (cost=0.00..26541.00 rows=100000 width=32) (actual rows=100000 loops=1) |
| Planning time: 0.075 ms |
| Execution time: 1866.407 ms |
EXPLAIN (ANALYZE, TIMING OFF) SELECT sort7(str) FROM tbl;
| QUERY PLAN |
| :--------------------------------------------------------------------------------------- |
| Seq Scan on tbl (cost=0.00..26541.00 rows=100000 width=32) (actual rows=100000 loops=1) |
| Planning time: 0.067 ms |
| Execution time: 1863.713 ms |
EXPLAIN (ANALYZE, TIMING OFF) SELECT sort8(str) FROM tbl;
| QUERY PLAN |
| :--------------------------------------------------------------------------------------- |
| Seq Scan on tbl (cost=0.00..26541.00 rows=100000 width=32) (actual rows=100000 loops=1) |
| Planning time: 0.074 ms |
| Execution time: 1569.376 ms |
db <> скрипка здесь
Последний сортируется без COLLATION
правил,это строго байтовые значения символов, что существенно дешевле.Но вам может или не может потребоваться порядок сортировки для другого языка.
Руководство по COLLATION
выражениям.