例如,
foo1
foo2
foo10
foo100
Run Code Online (Sandbox Code Playgroud)
而不是
foo1
foo10
foo100
foo2
Run Code Online (Sandbox Code Playgroud)
更新:对自己编码排序不感兴趣(虽然这本身就很有趣),但让数据库为我做排序.
您可以在order-by子句中使用函数.在这种情况下,您可以拆分字段的非数字和数字部分,并将它们用作两个排序条件.
select * from t
order by to_number(regexp_substr(a,'^[0-9]+')),
to_number(regexp_substr(a,'[0-9]+$')),
a;
Run Code Online (Sandbox Code Playgroud)
您还可以创建基于函数的索引来支持:
create index t_ix1
on t (to_number(regexp_substr(a, '^[0-9]+')),
to_number(regexp_substr(a, '[0-9]+$')),
a);
Run Code Online (Sandbox Code Playgroud)
如果“数字”的数量和最大长度受到限制,可以使用基于正则表达式的解决方案。
这个想法是:
假设:
lpad('1 ', 3000, '1 ')将失败,因为无法将填充的数字放入varchar2(4000))以下查询针对“短数字”情况进行了优化(请参阅 参考资料*?),并且需要 0.4 秒。然而,当使用这种方法时,您需要预先定义填充长度。
select * from (
select dbms_random.string('X', 30) val from xmltable('1 to 1000')
)
order by regexp_replace(regexp_replace(val, '(\d+)', lpad('0', 20, '0')||'\1')
, '0*?(\d{21}(\D|$))', '\1');
Run Code Online (Sandbox Code Playgroud)
尽管单独的natural_sort函数很方便,但在纯 SQL 中却有一个鲜为人知的技巧。
主要想法:
02之间排序。注意:这可能会导致>的“意外”排序(因为转换为),但是没有单一的答案应该如何排序13regexp_replace(val, '(^|\D)0+(\d+)', '\1\2')10.0210.102210.02.03"为""带有引号的文本可以正常工作'"'||regexp_replace(..., '([^0-9]+)', '","\1","')||'"'xmltablelength(length(num))||length(num)||num而不是lpad(num, 10, '0')因为后者不太紧凑并且不支持 11 位以上的数字。笔记:对于 1000 个长度为 30 的随机字符串的列表进行排序,响应时间约为 3-4 秒(随机字符串的生成本身需要 0.2 秒)。主要的时间消耗是xmltable将文本分割成行。如果使用 PL/SQL 而不是xmltable将字符串拆分为行,则对于相同的 1000 行,响应时间会减少到 0.4 秒。
以下查询对 100 个随机字母数字字符串执行自然排序(注意:它在 Oracle 11.2.0.4 中产生错误结果,但在 12.1.0.2 中有效):
select *
from (
select (select listagg(case when regexp_like(w, '^[0-9]')
then length(length(w))||length(w)||w else w
end
) within group (order by ord)
from xmltable(t.csv columns w varchar2(4000) path '.'
, ord for ordinality) q
) order_by
, t.*
from (
select '"'||regexp_replace(replace(
regexp_replace(val, '(^|\D)0+(\d+)', '\1\2')
, '"', '""')
, '([^0-9]+)', '","\1","')||'"' csv
, t.*
from (
select dbms_random.string('X', 30) val from xmltable('1 to 100')
) t
) t
) t
order by order_by;
Run Code Online (Sandbox Code Playgroud)
有趣的是,它order by可以在没有子查询的情况下表达,因此它是一个让您的审阅者疯狂的方便工具(它适用于 11.2.0.4 和 12.1.0.2):
select *
from (select dbms_random.string('X', 30) val from xmltable('1 to 100')) t
order by (
select listagg(case when regexp_like(w, '^[0-9]')
then length(length(w))||length(w)||w else w
end
) within group (order by ord)
from xmltable('$X'
passing xmlquery(('"'||regexp_replace(replace(
regexp_replace(t.val, '(^|\D)0+(\d+)', '\1\2')
, '"', '""')
, '([^0-9]+)', '","\1","')||'"')
returning sequence
) as X
columns w varchar2(4000) path '.', ord for ordinality) q
);
Run Code Online (Sandbox Code Playgroud)