没有正则表达式和函数的解决方案(假设t是包含源数据的表):
select * from t
order by
(
select
sum(
to_number(substr(
sections,
decode(level,
1,1,
instr(sections, '.', 1, level-1)+1
),
decode(instr(sections, '.', 1, level),
0, length(sections),
instr(sections, '.', 1, level)
-
decode(level,
1,1,
instr(sections, '.', 1, level-1)+1
)
)
))
* power(1000, 10-level)
)
from dual
connect by instr(sections,'.',1,level-1) > 0
)
主要思想是计算数字,它表示每行的优先级。假设我们有33.17.21.2值。该字符串可以被视为数字系统中的数字,基数为Q,如十六进制数字表示IPv4地址,然后转换为数字表示:
33*(Q^3) + 17*(Q^2) + 21*(Q^1) + 2*(Q^0)
例如,如果Q=100,那么来自exmple的数字是
33*1000000 + 17*10000 + 21*100 + 2*1 = 33172102
这种方法的第一个问题是每个级别数要求低于选择Q值。这是设计上的,不能被扼杀。
接下来就是我们根本不知道有多少级别,我们有7.1和2.2.2.2.2.2,而最短的级别是第一个。因此,在计算值时,它从某个固定功率N开始,然后降低Q的功效,因此,如果Q=100和N=3多路复用器序列以这些数字开头:
1000000, 10000, 100, 1, 1/100, 1/10000, 1/1000000, ...
在Q=1000和N=10之上的代码中,但这可能会根据所需参数进行更改。
由选择的Q值和Oracle number类型的精度限制的级别数。从理论上讲,通过将字符串拆分成部分,可以为更长的字符串构建表达式。
其余代码只是用于将字符串拆分为数字序列的分层查询。
更新
同样的方法可以很容易地用于字符串:'20'出现在'8'之前,因为缺少关于第二个数字的信息。如果我们将这两个值填充到某个固定长度,它会按预期排序:'008' < '020',因此只能处理字符串:
select * from t order by
(
select
listagg(
lpad(
substr(
sections,
decode( level,
1,1,
instr(sections, '.', 1, level-1)+1
),
decode(instr(sections, '.', 1, level),
0, length(sections),
instr(sections, '.', 1, level)
-
decode(level,
1, 1,
instr(sections, '.', 1, level-1)+1
)
)
),
8,'0'
),
'-'
) within group (order by level)
from dual
connect by instr(sections,'.',1,level-1) > 0
)
字符串长度限制为4000个字符,每个级别有9个数字,单个分隔符号(上例中为'-'),可以处理400级别的层次结构。
这种方法的主要缺点是内存消耗和比较速度。
另一方面,如果没有转换为数字,即使使用混合章节编号也是如此(例如'13.3.a.vii'或'III.A.13.2'(Ooops ... roman numerals处理不当)
在十进制数的情况下,只有带字符串的编号变量可以通过将数字转换为十六进制表示来压缩。使用4个十六进制符号,可以在每个级别处理16535个数字,并且可以使用8个符号 - 完整的32位数字,这对于大多数应用来说已经足够了。
select * from t order by
(
select
listagg(
lpad(
trim(to_char(
to_number(substr(
sections,
decode( level,
1,1,
instr(sections, '.', 1, level-1)+1
),
decode(instr(sections, '.', 1, level),
0, length(sections),
instr(sections, '.', 1, level)
-
decode(level,
1, 1,
instr(sections, '.', 1, level-1)+1
)
)
)),
'XXXXXXXX'
)),
4,'0'
),
'-'
) within group (order by level)
from dual
connect by instr(sections,'.',1,level-1) > 0
)
P.S。当然,可以使用选择列表中的所有表达式来检查计算值,而不是在order by中使用它。