SQL查询到listAgg和group by

时间:2016-05-13 17:18:12

标签: sql oracle listagg

我写了一个新的查询,它给我一个listAgg结果,如下面的

               Col A
 1000016932,1000020056,1000020100,1000020144,1000020243

Col A 1000016932,1000020056,1000020100,1000020144,1000020243

现在我想要的结果如下

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

3 个答案:

答案 0 :(得分: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;

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

结果:

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

答案 1 :(得分:1)

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

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

    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)}这样的东西。为了消除重复并以您想要的方式检索行 - 确保第二个表的rownumber大于第一个。这样你就得到 - {(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;

答案 2 :(得分:0)

  

我写了一个新的查询,它给我一个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