我目前正在使用这种方法在 PostgreSQL 中按字母顺序对字符串中的字母进行排序。还有其他有效的方法吗?
select string_agg(c, '') as s
from (select unnest(regexp_split_to_array('ijsAafhareDbv', '')) as c
order by c) as t;
s
--------------
ADaabefhijrsv
我目前正在使用这种方法在 PostgreSQL 中按字母顺序对字符串中的字母进行排序。还有其他有效的方法吗?
select string_agg(c, '') as s
from (select unnest(regexp_split_to_array('ijsAafhareDbv', '')) as c
order by c) as t;
s
--------------
ADaabefhijrsv
我创建了 3 个函数,一个使用我的查询,另一个使用 Laurenz 的查询,还有一个:我创建了一个用于排序的 Python(plpythonu) 函数。最后,我创建了一个包含 100000 行的表(我现在是从我的 Mac 笔记本电脑上做的),每行都包含使用此链接random_string
中的函数生成的随机 15 个字符串
create table t as select random_string(15) as s FROM generate_series(1,100000);
这是3个功能。
CREATE or REPLACE FUNCTION sort1(x TEXT) RETURNS TEXT AS $$
select string_agg(c, '') as s
from (select unnest(regexp_split_to_array($1, '')) as c
order by c) as t;
$$ LANGUAGE SQL IMMUTABLE;
CREATE or REPLACE FUNCTION sort2(x TEXT) RETURNS TEXT AS $$
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;
$$ LANGUAGE SQL IMMUTABLE;
create language plpythonu;
CREATE or REPLACE FUNCTION pysort(x text)
RETURNS text
AS $$
return ''.join(sorted(x))
$$ LANGUAGE plpythonu IMMUTABLE;
这些是EXPLAIN ANALYSE
所有三个的结果。
knayak=# EXPLAIN ANALYSE select sort1(s) FROM t;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------
Seq Scan on t (cost=0.00..26541.00 rows=100000 width=32) (actual time=0.266..7097.740 rows=100000 loops=1)
Planning time: 0.119 ms
Execution time: 7106.871 ms
(3 rows)
knayak=# EXPLAIN ANALYSE select sort2(s) FROM t;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------
Seq Scan on t (cost=0.00..26541.00 rows=100000 width=32) (actual time=0.418..7012.935 rows=100000 loops=1)
Planning time: 0.270 ms
Execution time: 7021.587 ms
(3 rows)
knayak=# EXPLAIN ANALYSE select pysort(s) FROM t;
QUERY PLAN
------------------------------------------------------------------------------------------------------------
Seq Scan on t (cost=0.00..26541.00 rows=100000 width=32) (actual time=0.060..389.729 rows=100000 loops=1)
Planning time: 0.048 ms
Execution time: 395.760 ms
(3 rows)
从这个分析中,事实证明 - Python 排序是最快的,与前 2 个排序没有显着差异。但需要实时检查我们系统中大型表的性能。
中实现的功能比我们用or实现的任何C
功能都要快得多。因此,您的函数在性能竞赛中遥遥领先。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
字符串 | 排序1 | 排序2 | 排序3 | 排序4 | 排序5 | 排序6 | 排序7 | 排序8 :---------------- | :---------------- | :---------------- | :---------------- | :---------------- | :---------------- | :---------------- | :---------------- | :-------------- tUkmori4D1rHhI1 | 114DhHiIkmorrtU | 114DhHiIkmorrtU | 114DhHiIkmorrtU | 114DhHiIkmorrtU | 114DhHiIkmorrtU | 114DhHiIkmorrtU | 114DhHiIkmorrtU | 114DHIUhikmorrt
EXPLAIN (ANALYZE, TIMING OFF) SELECT sort1(str) FROM tbl;
| 查询计划 | | :------------------------------------------------ -------------------------------------------------- | | Seq Scan on tbl (cost=0.00..26541.00 rows=100000 width=32) (实际 rows=100000 loops=1) | | 规划时间:0.053 ms | | 执行时间:2742.904 毫秒 |
EXPLAIN (ANALYZE, TIMING OFF) SELECT sort2(str) FROM tbl;
| 查询计划 | | :------------------------------------------------ -------------------------------------------------- | | Seq Scan on tbl (cost=0.00..26541.00 rows=100000 width=32) (实际 rows=100000 loops=1) | | 规划时间:0.105 ms | | 执行时间:2579.397 ms |
EXPLAIN (ANALYZE, TIMING OFF) SELECT sort3(str) FROM tbl;
| 查询计划 | | :------------------------------------------------ -------------------------------------------------- | | Seq Scan on tbl (cost=0.00..26541.00 rows=100000 width=32) (实际 rows=100000 loops=1) | | 规划时间:0.079 ms | | 执行时间:2191.228 毫秒 |
EXPLAIN (ANALYZE, TIMING OFF) SELECT sort4(str) FROM tbl;
| 查询计划 | | :------------------------------------------------ -------------------------------------------------- | | Seq Scan on tbl (cost=0.00..26541.00 rows=100000 width=32) (实际 rows=100000 loops=1) | | 规划时间:0.075 ms | | 执行时间:2194.780 ms |
EXPLAIN (ANALYZE, TIMING OFF) SELECT sort5(str) FROM tbl;
| 查询计划 | | :------------------------------------------------ -------------------------------------------------- | | Seq Scan on tbl (cost=0.00..26541.00 rows=100000 width=32) (实际 rows=100000 loops=1) | | 规划时间:0.083 ms | | 执行时间:1902.829 ms |
EXPLAIN (ANALYZE, TIMING OFF) SELECT sort6(str) FROM tbl;
| 查询计划 | | :------------------------------------------------ -------------------------------------------------- | | Seq Scan on tbl (cost=0.00..26541.00 rows=100000 width=32) (实际 rows=100000 loops=1) | | 规划时间:0.075 ms | | 执行时间:1866.407 ms |
EXPLAIN (ANALYZE, TIMING OFF) SELECT sort7(str) FROM tbl;
| 查询计划 | | :------------------------------------------------ -------------------------------------------------- | | Seq Scan on tbl (cost=0.00..26541.00 rows=100000 width=32) (实际 rows=100000 loops=1) | | 规划时间:0.067 ms | | 执行时间:1863.713 ms |
EXPLAIN (ANALYZE, TIMING OFF) SELECT sort8(str) FROM tbl;
| 查询计划 | | :------------------------------------------------ -------------------------------------------------- | | Seq Scan on tbl (cost=0.00..26541.00 rows=100000 width=32) (实际 rows=100000 loops=1) | | 规划时间:0.074 ms | | 执行时间:1569.376 ms |
db<>在这里摆弄
最后一种是没有COLLATION
规则的,严格按字符的字节值排序,这要便宜得多。但是您可能需要也可能不需要不同语言环境的排序顺序。
如果你想要一个没有正则表达式的解决方案,你可以使用这个:
WITH t(s) AS (VALUES ('amfjwzeils'))
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;
string_agg
------------
aefijlmswz
(1 row)
我会基准测试哪个解决方案更快。