Sql 使用多个小数点对Oracle中的记录进行排序(.)

Sql 使用多个小数点对Oracle中的记录进行排序(.),sql,oracle,sorting,oracle10g,decimal-point,Sql,Oracle,Sorting,Oracle10g,Decimal Point,更新: ORACLE版本10G 我在Oracle中有一个记录列表,如下所示,这些实际上是各种书籍的部分 记录按以下格式生成 [主主题][子主题][第一级章节]。[最后一级部分] Sections -------- 1 7.1 6.2 7.1 7.4 6.8.3 6.8.2 10 1.1 7.6 6.1 11 8.3 8.5 1.1.2 6.4 6.6 8.4 1.1.6 6.8.1 7.7.1 7.5 7.3 我想按如下方式订购 1 1.1 1.1.2 1.1.6 6.2

更新:

ORACLE版本10G

我在Oracle中有一个记录列表,如下所示,这些实际上是各种书籍的部分
记录按以下格式生成

[主主题][子主题][第一级章节]。[最后一级部分]

Sections
--------
1
7.1
6.2 
7.1
7.4
6.8.3
6.8.2
10
1.1
7.6
6.1
11
8.3
8.5
1.1.2
6.4
6.6
8.4
1.1.6
6.8.1
7.7.1
7.5
7.3
我想按如下方式订购

 1
 1.1
 1.1.2
 1.1.6
 6.2    
 6.4    
 6.5    
 6.6    
 6.7    
 6.8.1    
 6.8.2    
 6.8.3    
 7.2    
 7.3    
 7.4    
 7.5    
 7.6    
 7.7.1    
 7.7.2    
 8.3    
 8.4    
 8.5
 10
但由于字段不是
数字
数据类型
,排序结果如下

1
10
1.1
1.1.2
1.1.6
....
.....
8.5
我怎样才能把它们分类呢。由于存在多个小数点,我无法将它们转换为数字


oracle
中是否有支持这种排序技术的功能

如果级别数是固定的(例如最多4个),您可以使用此功能:

ORDER BY 
    TO_NUMBER(REGEXP_SUBSTR(Sections, '\d+', 1, 1)) NULLS FIRST, 
    TO_NUMBER(REGEXP_SUBSTR(Sections, '\d+', 1, 2)) NULLS FIRST, 
    TO_NUMBER(REGEXP_SUBSTR(Sections, '\d+', 1, 3)) NULLS FIRST, 
    TO_NUMBER(REGEXP_SUBSTR(Sections, '\d+', 1, 4)) NULLS FIRST

当已知最大深度时,可以将截面拆分为子截面:

SQL> SELECT SECTION FROM DATA
  2   ORDER BY to_number(regexp_substr(SECTION, '[^.]+', 1, 1)) NULLS FIRST,
  3            to_number(regexp_substr(SECTION, '[^.]+', 1, 2)) NULLS FIRST,
  4            to_number(regexp_substr(SECTION, '[^.]+', 1, 3)) NULLS FIRST;

SECTION
-------
1
1.1
1.1.2
1.1.6
6.1
6.2
[...]
8.5
10
11
如果子节的最大深度未知(但在8位字符数据库中可能小于几百个,在ANSI字符数据库中可能小于几千个),则可以定义一个函数,将不可排序的数字转换为可排序的字符:

SQL> CREATE OR REPLACE FUNCTION order_section (p_section VARCHAR2)
  2     RETURN VARCHAR2 IS
  3     l_result VARCHAR2(4000);
  4  BEGIN
  5     FOR i IN 1..regexp_count(p_section, '[^.]+') LOOP
  6        l_result := l_result
  7                    || CASE WHEN i > 1 THEN '.' END
  8                    || CHR(64+regexp_substr(p_section, '[^.]+', 1, i));
  9     END LOOP;
 10     RETURN l_result;
 11  END;
 12  /

Function created

SQL> SELECT SECTION, order_section(SECTION)
  2    FROM DATA
  3   ORDER BY 2;

SECTION ORDER_SECTION(SECTION)
------- -------------------------
1       A
1.1     A.A
1.1.2   A.A.B
1.1.6   A.A.F
6.1     F.A
6.2     F.B
[...]
8.5     H.E
10      J
11      K

以下是我最后针对一般情况(点的数量未知时)提出的解决方案-保留第一个点的原样,并将所有其他点替换为零,这样您就只有一个浮点数,您可以通过以下方式应用顺序:

SELECT SECTIONS FROM T_TABLE
ORDER BY TO_NUMBER(SUBSTR(SECTIONS,0,DECODE(INSTR(SECTIONS,'.'),0,LENGTH(SECTIONS)+1,INSTR(SECTIONS,'.'))) ||
REPLACE(SUBSTR(SECTIONS,DECODE(INSTR(SECTIONS,'.'),0,LENGTH(SECTIONS)+1,INSTR(SECTIONS,'.'))),'.','0'))


使用常规表达式可以更优雅地重写它,但我对它们不是很熟悉,所以只使用了基本的Oracle函数:)

您可以尝试一下-

最佳部分-无需担心级别的深度

SELECT
Section
FROM SectionData
ORDER BY
CAST (CASE WHEN CHARINDEX('.',Section) > 0
THEN SUBSTRING(Section,0,CHARINDEX('.',Section))
ELSE Section END AS INT)
,REPLACE(Section,'0',':')
工作原理:-

因此,首先根据第一个点之前的整数进行排序

由于您的节是字符串类型,因此排序是基于ASCII代码进行的。此外,你的部分中最重要的部分是第一个点之前的第一组数字列表

这是您的第二个排序标准

现在,将产生所有问题的是0,因此将“0”替换为ASCII值高于9的任何内容


我已经用一些基本组合(包括更高的深度)对它进行了测试-在使用它之前,请继续正确地测试它。

我认为最简单的。。。复制并运行以查看输出:

SELECT val FROM  --,to_number(trim(BOTH '.' FROM substr(val, 1, 2))) num_val,
(
 SELECT '1' val FROM dual
 UNION ALL
 SELECT '7.1' FROM dual
 UNION ALL
 SELECT '6.2' FROM dual
 UNION ALL
 SELECT '7.1' FROM dual
 UNION ALL
 SELECT '7.4' FROM dual
 UNION ALL
 SELECT '6.8.3' FROM dual
 UNION ALL
 SELECT '6.8.2' FROM dual
 UNION ALL
 SELECT '10' FROM dual
 UNION ALL
 SELECT '1.1' FROM dual
 UNION ALL
 SELECT '7.6' FROM dual
 UNION ALL
 SELECT '6.1' FROM dual
 UNION ALL
 SELECT '11' FROM dual
 UNION ALL
 SELECT '8.3' FROM dual
 UNION ALL
 SELECT '8.5' FROM dual
 UNION ALL
 SELECT '1.1.2' FROM dual
 UNION ALL
 SELECT '6.4' FROM dual
 UNION ALL
 SELECT '6.6' FROM dual
 UNION ALL
 SELECT '8.4' FROM dual
 UNION ALL
 SELECT '1.1.6' FROM dual
 UNION ALL
 SELECT '6.8.1' FROM dual
 UNION ALL
 SELECT '7.7.1' FROM dual
 UNION ALL
 SELECT '7.5' FROM dual
 UNION ALL
 SELECT '7.3' FROM dual
)
ORDER BY to_number(trim(BOTH '.' FROM substr(val, 1, 2)))

不带regexp和函数的解决方案(假设
t
是一个包含源数据的表):

主要思想是计算数字,它表示每行的优先级。假设我们有
33.17.21.2
值。此字符串可以被视为基数为
Q
的等比数字系统中的数字,就像十六进制数字表示IPv4地址一样,然后转换为数字表示:
33*(Q^3)+17*(Q^2)+21*(Q^1)+2*(Q^0)

例如,如果
Q=100
,则示例中的数字为

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…处理不当)

对于十进制数,只有带字符串的编号变量可以通过将数字转换为十六进制表示来压缩。使用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.当然,可以在选择列表中使用上面的所有表达式来检查计算值,而不是按的顺序使用它。

字符串中的“.”符号数是固定的(即不超过两个)还是无法确定?它最多可以超过2个6个非常罕见(几乎从不)更重要的是,我只是保持了示例的简单性。理想情况下,从长远来看,将它们拆分为各自的列/表。存储这样的数据违反了1NF。首先用
NULLS来更正它,
123.17.32.11.8如何?第二级没有排序。我在输入中没有看到“123.17.32.11.8”。我使用了我看到的,而不是ip地址号rs或诸如此类。如果他把它放在例子中,那么答案会有所不同。如问题中所述,源数据是各种书籍的
部分,因此书中可能有超过99个部分
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
  )