2016-05-13 96 views
1

我写了一个新的查询,这让我一个LISTAGG结果如下面
SQL查询来LISTAGG和group by

   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

请注意,我不能硬编码的水平,因为每个字符串可以是任何给定的长度

+0

简单来说什么我想要的是在listagg中获取案例,然后将该列表结果转换为行。因此,如果listagg包含(abc; def)将其转换为Col_1 = abc column2 = def – Mohamed

+0

预期的结果看起来不合逻辑。问题不明确。 –

+0

改变这个问题也许它会有道理。 – Mohamed

回答

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 
+0

I试过这个,它可以工作,但是如果我有两行而不是'1000016932,1000020056,1000020100,1000020144,1000020243',会发生什么情况。可以说我现在有两排。是否有可能使这个查询只做每行的操作。这就是说,table_1 col_a包含两行第1行= 1,2和第2行= 3,4,5。我可以让它返回(1,2),(3,4),(3,5),(4,5)? – Mohamed

+1

这是可能的,但在志愿者为你做更多工作之前(我不应该为每个人说 - 在我**之前做更多的工作),请提前说明你的所有需求,不要一次添加一个。这需要时间来帮助其他海报。谢谢你的理解! – mathguy

+0

是的,先生你是正确的,对此感到抱歉。这是最后的要求,没有什么别的了 – Mohamed

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; 
+0

这也适用,但同样的评论也适用于这个答案。 – Mohamed

+0

我会更新我的答案。 – ruudvan

+0

刚刚意识到我正在比较实际值而不是行号或级别。让我解决这个问题。 – ruudvan

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