【问题标题】:Sorting comma delimited datasets in row按行对逗号分隔的数据集进行排序
【发布时间】:2019-07-03 13:01:35
【问题描述】:

这是给定的

Numbers         Powers
4,5,1           WATER,FIRE
6,3,9           ICE,WATER,FIRE

我的要求是(排序)

Numbers               Powers
1,4,5                 FIRE,WATER
3,6,9                 FIRE,ICE,WATER .

我希望它按顺序排列!在数据库中怎么做?

【问题讨论】:

  • Splitting string into multiple rows in Oracle 开头。也停在那里。将 CSV 数据放入单独的记录后,不要再次将其汇总到 CSV 中。
  • DB2 还是 Oracle?这是两种截然不同的数据库产品
  • 到底为什么要将逗号分隔的值存储在单列中?这是一个非常非常糟糕的主意

标签: sql oracle db2 listagg


【解决方案1】:

将列拆分为行,然后将它们聚合回来,排序。

SQL> with test (id, num, pow) as
  2    (select 1, '4,5,1', 'water,fire'     from dual union all
  3     select 2, '6,3,9', 'ice,water,fire' from dual
  4    ),
  5  temp as
  6    -- split columns to rows
  7    (select id,
  8            regexp_substr(num, '[^,]+', 1, column_value) num1,
  9            regexp_substr(pow, '[^,]+', 1, column_value) pow1
 10     from test join table(cast(multiset(select level from dual
 11                                        connect by level <= regexp_count(num, ',') + 1
 12                                       ) as sys.odcinumberlist)) on 1 = 1
 13    )
 14    -- aggregate them back, sorted
 15  select id,
 16         listagg(num1, ',') within group (order by to_number(num1)) num_result,
 17         listagg(pow1, ',') within group (order by pow1) pow_result
 18  from temp
 19  group by id;

        ID NUM_RESULT                     POW_RESULT
---------- ------------------------------ ------------------------------
         1 1,4,5                          fire,water
         2 3,6,9                          fire,ice,water

SQL>

【讨论】:

  • 你是圣人 ;-)
  • 如果 POW 中的值多于 NUM 中的值,则此解决方案不起作用。 db<>fiddle
【解决方案2】:

Oracle 设置

CREATE TABLE test_data ( Numbers, Powers ) AS
SELECT '4,5,1', 'WATER,FIRE'     FROM DUAL UNION ALL
SELECT '6,3,9', 'ICE,WATER,FIRE' FROM DUAL UNION ALL
SELECT '7',     'D,B,E,C,A'      FROM DUAL

查询

SELECT (
         SELECT LISTAGG( TO_NUMBER( REGEXP_SUBSTR( t.numbers, '\d+', 1, LEVEL ) ), ',' )
                  WITHIN GROUP ( ORDER BY TO_NUMBER( REGEXP_SUBSTR( t.numbers, '\d+', 1, LEVEL ) ) )
         FROM   DUAL
         CONNECT BY LEVEL <= REGEXP_COUNT( t.numbers, ',' ) + 1
       ) AS numbers,
       (
         SELECT LISTAGG( REGEXP_SUBSTR( t.powers, '[^,]+', 1, LEVEL ), ',' )
                  WITHIN GROUP ( ORDER BY REGEXP_SUBSTR( t.powers, '[^,]+', 1, LEVEL ) )
         FROM   DUAL
         CONNECT BY LEVEL <= REGEXP_COUNT( t.powers, ',' ) + 1
       ) AS numbers
FROM   test_data t

输出

数字 |数字 :-------- | :------------- 1,4,5 |消防用水 3,6,9 |火、冰、水 7 | A,B,C,D,E

db小提琴here

【讨论】:

    【解决方案3】:

    您可以尝试以下方法:

    我使用了该表,因为我需要一些值来获得不同的值。这里我使用了ROWID

    SELECT
        ID,
        LISTAGG(NUM, ',') WITHIN GROUP(
            ORDER BY
                NUM
        ) AS NUM,
        LISTAGG(POW, ',') WITHIN GROUP(
            ORDER BY
                POW
        ) AS POW
    FROM
        (
            SELECT
                DISTINCT ROWID,
                ID,
                REGEXP_SUBSTR(NUM, '[^,]+', 1, LEVEL) NUM,
                REGEXP_SUBSTR(POW, '[^,]+', 1, LEVEL) POW
            FROM
                TEST
            CONNECT BY REGEXP_SUBSTR(NUM, '[^,]+', 1, LEVEL) IS NOT NULL
                       OR REGEXP_SUBSTR(POW, '[^,]+', 1, LEVEL) IS NOT NULL
        )
        GROUP BY ID
        ORDER BY ID;
    

    db<>fiddle demo

    干杯!!

    ---- 更新 ----

    正如评论中提到的它正在生成重复项,我将整个查询重新构建如下:

    SELECT
        ID,
        LISTAGG(C_S.NUM, ',') WITHIN GROUP(
                    ORDER BY
                        C_S.NUM
                ) AS NUM,
        LISTAGG(C_S.POW, ',') WITHIN GROUP(
                    ORDER BY
                        C_S.POW
                ) AS POW
    FROM
    (SELECT
        T.ID,
        REGEXP_SUBSTR(T.NUM, '[^,]+', 1, NUMS_COMMA.COLUMN_VALUE) NUM,
        REGEXP_SUBSTR(T.POW, '[^,]+', 1, NUMS_COMMA.COLUMN_VALUE) POW
    FROM
        TEST T,
        TABLE ( CAST(MULTISET(
            SELECT
                LEVEL
            FROM
                DUAL
            CONNECT BY
                LEVEL <= GREATEST(LENGTH(REGEXP_REPLACE(T.NUM, '[^,]+')),
                LENGTH(REGEXP_REPLACE(T.POW, '[^,]+'))) + 1
        ) AS SYS.ODCINUMBERLIST) ) NUMS_COMMA) C_S
     GROUP BY ID;
    

    db<>fiddle demo updated

    干杯!!

    【讨论】:

    • 这将从CONNECT BY 生成成指数的许多行,因为它将在行之间交叉关联。是的,DISTINCT 子句将消除重复项,但效率非常低。删除DISTINCT 子句,您将看到生成了多少重复项,需要删除db<>fiddle
    • @MT0 ,是的,它正在生成重复项。感谢您的宝贵评论。我现在更改了查询。你能复习一下吗?我认为现在它是完美的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-06
    • 2020-03-14
    • 2019-04-21
    • 1970-01-01
    • 2017-07-17
    相关资源
    最近更新 更多