【问题标题】:To sort the letters in a string alphabetically in PostgreSQL在 PostgreSQL 中按字母顺序对字符串中的字母进行排序
【发布时间】:2018-05-18 17:31:34
【问题描述】:

我目前正在使用这种方法在 PostgreSQL 中按字母顺序对字符串中的字母进行排序。还有其他有效的方法吗?

select string_agg(c, '') as s
from   (select unnest(regexp_split_to_array('ijsAafhareDbv', '')) as c 
        order  by c) as t; 

       s   
 --------------
 ADaabefhijrsv

【问题讨论】:

  • 这似乎是一个相当合理的解决方案。
  • @Gordon :有没有一种方法可以避免正则表达式,因为它的处理速度有点慢?虽然我们不需要每天都运行它,但偶尔需要将它应用于海量数据。任何使用外部库的程序 (plpgsql) 方法 /indexing/ 也可以,如果它运行良好的话。
  • 记住pl/perlpl/python 或其他第三方程序语言。对于此类任务,它可能会更快。

标签: sql postgresql plpgsql postgresql-performance


【解决方案1】:

我创建了 3 个函数,一个使用我的查询,另一个使用 Laurenz 的查询,还有一个:我创建了一个用于排序的 Python(plpythonu) 函数。最后,我创建了一个有 100000 行的表(我现在是从我的 Mac 笔记本电脑上做的) 每个包含使用random_string 函数在此Link 中生成的随机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 个排序没有太大区别。不过需要实时检查我们系统中大型表的性能。

【讨论】:

  • 本机函数 - 用 C 语言实现,如 Python 库中的 sorted 方法必须是最快的代码。
【解决方案2】:

C 中实现的功能大大比我们用LANGUAGE sqlplpgsql 实现的任何功能都要快。所以your plpythonu function在表演比赛中大胜。

plpythonu 是一种不受信任的程序语言。默认情况下不安装它,只有超级用户可以使用不受信任的语言创建函数。您需要了解安全隐患。而且大多数云服务根本不提供不受信任的语言。
The current manual (quote from pg 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 毫秒 |

db小提琴here

最后一种没有COLLATION 规则,严格按字符的字节值排序,这要便宜得多。但您可能需要不同区域设置的排序顺序。

The manual about COLLATION expressions.

【讨论】:

  • 太棒了!谢谢欧文。
【解决方案3】:

如果你想要一个没有正则表达式的解决方案,你可以使用这个:

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)

我会衡量哪种解决方案更快。

【讨论】:

  • 谢谢劳伦兹。我做了(某种)基准测试并发布了答案。请让我知道我是否遗漏或必须修改任何内容以使其更好地进行比较。
猜你喜欢
  • 2017-10-04
  • 2015-07-23
  • 1970-01-01
  • 2016-12-05
  • 2018-11-20
  • 2021-05-08
  • 2013-02-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多