【问题标题】:SQL query to listAgg and group by对 listAgg 和分组依据的 SQL 查询
【发布时间】:2016-05-13 17:18:12
【问题描述】:

我写了一个新查询,它得到了一个 listAgg 结果,如下所示

               Col A
 1000016932,1000020056,1000020100,1000020144,1000020243

现在我想要的结果如下

col C col D 1000016932 1000020056 1000016932 1000020100 1000016932 1000020144 1000016932 1000020243 1000020056 1000020100 1000020056 1000020144 ...and so on

请注意,我无法对级别进行硬编码,因为每个字符串可以是任意给定长度

【问题讨论】:

  • 简单来说,我想要的是在 listagg 中获取案例,然后将 listagg 结果转换为行。因此,如果 listagg 包含 (abc; def) 将其转换为 Col_1 = abc column2 =def
  • 预期结果似乎不合逻辑。问题不清楚。
  • 换个问题也许会有意义。
  • 您在问题和评论中显示的内容有不同的含义。您是否尝试为输入行中两个不同“数字”的每种可能组合获取一行?使用评论中示例问题中的逻辑,您不仅想要 Col1 = abc 和 Col2 = def,还需要 Col1 = def 和 Col2 = abc 的第二行。请澄清。
  • Sooo... 您有一个带有逗号分隔值的长字符串,并且想要解析该字符串并将其转换为行。但是......只有一半的字符串......然后在第二列中的另一半......任意?还是不清楚。

标签: sql oracle listagg


【解决方案1】:
with table_1 (colA) as (
        select '1000016932,1000020056,1000020100,1000020144,1000020243' from dual
     ),
     prep (lvl, token) as (
        select level, regexp_substr(colA, '[^,]+', 1, level) from table_1
        connect by level <= regexp_count(colA, ',') + 1
        and colA = prior colA
        and prior sys_guid() is not null
     )
select p1.token as token_1, p2.token as token_2 
from prep p1 join prep p2 on p1.lvl < p2.lvl;

这假定逗号之间没有空值(您没有两个连续的逗号,它们之间没有任何内容,在序列中标记为“空”)。

结果:

TOKEN_1    TOKEN_2
---------- ----------
1000016932 1000020056
1000016932 1000020100
1000016932 1000020144
1000016932 1000020243
1000020056 1000020100
1000020056 1000020144
1000020056 1000020243
1000020100 1000020144
1000020100 1000020243
1000020144 1000020243

要允许输入表中有几行(假设初始表中有某种类型的 row_id 列):

with table_1 (row_id, colA) as (
        select 101, '1000016932,1000020056,1000020100,1000020144,1000020243' from dual union all
        select 102, '1000040042,1000045543,1000045664'                       from dual
     ),
     prep (lvl, row_id, token) as (
        select level, row_id, regexp_substr(colA, '[^,]+', 1, level) from table_1
        connect by level <= regexp_count(colA, ',') + 1
        and row_id = prior row_id
        and prior sys_guid() is not null
     )
select p1.row_id, p1.token as token_1, p2.token as token_2 
from prep p1 join prep p2 on p1.row_id = p2.row_id and p1.lvl < p2.lvl
order by row_id, token_1;

结果:

    ROW_ID TOKEN_1    TOKEN_2
---------- ---------- ----------
       101 1000016932 1000020144
       101 1000016932 1000020056
       101 1000016932 1000020100
       101 1000016932 1000020243
       101 1000020056 1000020243
       101 1000020056 1000020100
       101 1000020056 1000020144
       101 1000020100 1000020243
       101 1000020100 1000020144
       101 1000020144 1000020243
       102 1000040042 1000045543
       102 1000040042 1000045664
       102 1000045543 1000045664

【讨论】:

  • 我试过了,但是如果我有两行而不是'1000016932,1000020056,1000020100,1000020144,1000020243'会发生什么。可以说我现在有两行。是否可以让这个查询只做每行的操作。也就是说,table_1 col_a 包含两行 Row 1= 1,2 和 ROW 2 = 3,4,5。我可以让它返回 (1,2), (3,4), (3,5), (4,5) 吗?
  • 这是可能的,但在志愿者为您做更多工作之前(我不应该代表所有人 - 在 做更多工作之前)请提前说明您的所有要求, 不要一次添加一个。这占用了帮助其他海报的时间。感谢您的理解!
  • 是的,先生,您是正确的,对此感到抱歉。这是最后一个要求,没有别的了
【解决方案2】:

如果我理解正确,您需要在逗号分隔的字符串中获取所有值对组合,其中顺序不重要,不包括 (1,1)、(2,2) 等相同的值对。

第一步是将字符串转换为行并选择一个行号以及值 -

    SELECT ROWNUM AS r,
           REGEXP_SUBSTR (col_A,
                          '(.*?)(,|$)',
                          1,
                          LEVEL,
                          NULL,
                          1)
              val
      FROM my_table
CONNECT BY LEVEL <= REGEXP_COUNT (COL_A, ',') + 1;

然后与自身进行交叉连接。然而,这会给你同一对两次。所以像{(1,1), (1,2), (1,3), (2,1), (2,2), (2,3), (3,1), (3,2), (3,3)}。为了消除重复并以您想要的方式检索行 - 确保第二个表的行号大于第一个。这样你会得到 - {(1,2), (1,3), (2,3)}。

所以最终的查询看起来像 -

WITH my_table
     AS (SELECT '1000016932,1000020056,1000020100,1000020144,1000020243'
                   AS col_A
           FROM DUAL),
     vals
     AS (    SELECT ROWNUM AS r,
                    REGEXP_SUBSTR (col_A,
                                   '(.*?)(,|$)',
                                   1,
                                   LEVEL,
                                   NULL,
                                   1)
                       val
               FROM my_table
         CONNECT BY LEVEL <= REGEXP_COUNT (COL_A, ',') + 1)
SELECT v_a.val AS col_B, v_B.val AS col_C
  FROM vals v_A
       CROSS JOIN vals v_B
 WHERE v_B.val > v_A.val;

编辑

因为可能有多行,所以最好使用某种 ID 列,使用它可以将行连接在一起。所以在这个例子中 -

ID  COL_A
1   1,2,3,4
2   5,6,7

您唯一需要做的就是在拆分逗号分隔字符串时根据 ID 选择唯一行。

WITH my_table
     AS (SELECT 1 AS id, '1,2,3,4' AS col_A FROM DUAL
         UNION ALL
         SELECT 2, '5,6,7' FROM DUAL),
     vals
     AS (    SELECT DISTINCT id,
                             REGEXP_SUBSTR (col_A,
                                            '(.*?)(,|$)',
                                            1,
                                            LEVEL,
                                            NULL,
                                            1)
                                val
               FROM my_table
         CONNECT BY LEVEL <= REGEXP_COUNT (COL_A, ',') + 1)
  SELECT v_a.val AS col_B, v_B.val AS col_C
    FROM vals v_A
         JOIN vals v_B ON v_A.id = v_B.id
   WHERE v_B.val > v_A.val;

编辑 2

我意识到我在比较实际值,这是不正确的。它会强制所有值都是整数。这是一个允许整数或字符串的查询。

WITH my_table
     AS (SELECT 1 AS id, '1,2,3,4' AS col_A FROM DUAL
         UNION ALL
         SELECT 2, '5,6,7' FROM DUAL
         UNION ALL
         SELECT 3, 'a,b,c' FROM DUAL),
     vals
     AS (    SELECT DISTINCT id,
                             REGEXP_SUBSTR (col_A,
                                            '(.*?)(,|$)',
                                            1,
                                            LEVEL,
                                            NULL,
                                            1)
                                val
               FROM my_table
         CONNECT BY LEVEL <= REGEXP_COUNT (COL_A, ',') + 1
           ORDER BY id, val),
     vals_r AS (SELECT ROWNUM AS r, vals.* FROM vals)
SELECT v_a.val AS col_B, v_B.val AS col_C
  FROM vals_r v_A
       JOIN vals_r v_B ON v_A.id = v_B.id
 WHERE v_B.r > v_A.r;

【讨论】:

  • 这也有效,但同样的评论也适用于这个答案。
  • 刚刚意识到我在比较实际值而不是行号或级别。让我解决这个问题。
【解决方案3】:

我写了一个新查询,它得到了一个 listAgg 结果,如下所示
...
现在我想要的结果如下

您想退后一步,因为这看起来像是 XY problem。如果您将数据放在行中,然后将其聚合,然后想再次将其溢出到行中,那么您可以通过不首先进行聚合来更有效地执行此操作。

假设你有一张桌子:

CREATE TABLE table_name ( id, value ) AS
SELECT 1, 1 FROM DUAL UNION ALL
SELECT 1, 2 FROM DUAL UNION ALL
SELECT 2, 3 FROM DUAL UNION ALL
SELECT 2, 4 FROM DUAL UNION ALL
SELECT 2, 5 FROM DUAL;

现在你可以聚合它了:

SELECT id,
       LISTAGG( value, ',' ) WITHIN GROUP ( ORDER BY value ) AS "VALUES"
FROM   table_name
GROUP BY id;

这会给出这个:

        ID VALUES
---------- ------
         1 1,2
         2 3,4,5

然后再次开始将值拆分为行...

但是通过只执行自连接而不首先进行聚合来获得所有组合要简单得多:

SELECT a.id,
       a.value AS value1,
       b.value AS value2
FROM   table_name a
       INNER JOIN table_name b
       ON ( a.id = b.id AND a.value < b.value )

这将为您提供输出:

       ID     VALUE1     VALUE2
---------- ---------- ----------
         1          1          2 
         2          3          4 
         2          4          5 
         2          3          5 

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多