我在下表中遇到了Postgres ORDER BY
问题:
em_code name
EM001 AAA
EM999 BBB
EM1000 CCC
要在表格中插入新记录,
SELECT * FROM employees ORDER BY em_code DESC
ec_alpha
ec_num
ec_num++
ec_alpha
当em_code
到达EM1000时,上述算法失败。
第一步将返回EM999而不是EM1000,它将再次生成EM1000为新em_code
,打破了唯一键约束。
知道如何选择EM1000吗?
答案 0 :(得分:8)
原因是字符串按字母顺序排序(而不是像你想要的那样在数字上排序)和1之前的排序。 你可以像这样解决它:
SELECT * FROM employees ORDER BY substring(em_code, 3)::int DESC
从em_code
中删除冗余的'EM'会更有效 - 如果可以的话 - 并保存一个整数来开始。
从字符串中删除任何和所有非数字:
SELECT regexp_replace(em_code, E'\\D','','g')
FROM employees
\D
是“非数字”的正则表达式class-shorthand
'g'
作为第四个参数是“全局”开关,用于将替换应用于字符串中的每个匹配项,而不仅仅是第一个。
所以我用空字符串替换每个非数字,只用字符串中的数字来提取数字。
答案 1 :(得分:5)
您可以采取的一种方法是为此创建naturalsort
函数。这是一个由Postgres传奇RhodiumToad撰写的例子。
create or replace function naturalsort(text)
returns bytea language sql immutable strict as $f$
select string_agg(convert_to(coalesce(r[2], length(length(r[1])::text) || length(r[1])::text || r[1]), 'SQL_ASCII'),'\x00')
from regexp_matches($1, '0*([0-9]+)|([^0-9]+)', 'g') r;
$f$;
来源:http://www.rhodiumtoad.org.uk/junk/naturalsort.sql
要使用它,只需按以下方式调用您的订单中的功能:
SELECT * FROM employees ORDER BY naturalsort(em_code) DESC
答案 2 :(得分:4)
这总是出现在问题和我自己的发展中,我终于厌倦了这样做的棘手方法。我终于崩溃并将其实现为PostgreSQL扩展:
https://github.com/Bjond/pg_natural_sort_order
可以免费使用MIT许可证。
基本上它只是对字符串中的数字(零预先挂起的数字)进行规范化,这样就可以创建一个索引列来进行全速排序。自述文件解释道。
优点是你可以有一个触发器来完成工作,而不是你的应用程序代码。它将在PostgreSQL服务器上以机器速度计算,并且迁移添加列变得简单而快速。
答案 3 :(得分:3)
你可以使用这一行 “ORDER BY length(substring(em_code FROM'[0-9] +')),em_code”
答案 4 :(得分:1)
我在这个相关问题中详细描述了这个:
Humanized or natural number sorting of mixed word-and-number strings
(我将此答案仅作为有用的交叉引用发布,因此它是社区维基)。
答案 5 :(得分:1)
我想出了一些不同的东西。
基本思想是创建元组(integer, string)
的数组,然后按它们进行排序。魔术数字2147483647是int32_max,用于将字符串按数字排序。
ORDER BY ARRAY(
SELECT ROW(
CAST(COALESCE(NULLIF(match[1], ''), '2147483647') AS INTEGER),
match[2]
)
FROM REGEXP_MATCHES(col_to_sort_by, '(\d*)|(\D*)', 'g')
AS match
)
答案 6 :(得分:1)
从 Postgres 9.6 开始,可以指定一个排序规则来自然地对带有数字的列进行排序。
https://www.postgresql.org/docs/10/collation.html
-- First create a collation with numeric sorting
CREATE COLLATION numeric (provider = icu, locale = 'en@colNumeric=yes');
-- Alter table to use the collation
ALTER TABLE "employees" ALTER COLUMN "em_code" type TEXT COLLATE numeric;
现在只需按原样查询即可。
SELECT * FROM employees ORDER BY em_code
在我的数据上,我得到的结果是这样的(注意它也会对外来数字进行排序):
值 |
---|
0 |
0001 |
001 |
1 |
2 |
3 |
4 |
5 |
06 |
6 |
7 |
8 |
09 |
9 |
10 |
12 |
13 |
۱۳ |
14 |
15 |
16 |
答案 7 :(得分:0)
答案 8 :(得分:0)
以下解决方案结合了 another question 中提出的各种想法,以及 classic solution 中的一些想法:
create function natsort(s text) returns text immutable language sql as $$
select string_agg(r[1] || E'\x01' || lpad(r[2], 20, '0'), '')
from regexp_matches(s, '(\D*)(\d*)', 'g') r;
$$;
这个函数的设计目标是简单和纯字符串操作(没有自定义类型和数组),所以它可以很容易地用作一个嵌入式解决方案,并且很容易被索引。
注意:如果您希望数字超过 20
位,则必须将函数中的硬编码最大长度 20
替换为合适的更大的长度。请注意,这将直接影响结果字符串的长度,因此不要将该值设置为大于所需的值。