Oracle将列分隔为多行

时间:2017-02-22 23:29:05

标签: sql oracle

我正在寻找一个oracle查询,它将转换分隔的列(2列为多行并将其与另一个表连接

表1

id  - col1     - col2                                            
1   - PC1,PC2  - F1,F2 

表2

 co1  - co2
 F1   - V1
 F2   - V2

我正在寻找结果

1,PC1,F1,V1   
1,PC1,F2,V2
1,PC2,F1,V1
1,PC2,F2,V2

我试过

with EXTED as (SELECT id,col1,trim(COLUMN_VALUE) col2
FROM Table1,
  xmltable(('"'
  || REPLACE(col2, ',', '","')
 || '"'))) select  FG.id,FG.col2,
 trim(COLUMN_VALUE) col2,VF.co2 from EXTED FG ,Table2 VF,
 xmltable(('"'
  || REPLACE(col2, ',', '","')
 || '"'))
 where FG.col2 = VF.col1

但这需要花费大量时间才能得出结果。有没有更好的方法来获得结果..?

2 个答案:

答案 0 :(得分:2)

您的查询可以写成:

SELECT id,
       x1.COLUMN_VALUE.getStringVal() AS col1,
       x2.COLUMN_VALUE.getStringVal() AS col2,
       t2.col2
FROM   Table1 t1
       CROSS JOIN
       xmltable( ('"'|| REPLACE(t1.col1, ',', '","')|| '"') ) x1
       CROSS JOIN
       xmltable( ('"'|| REPLACE(t1.col2, ',', '","')|| '"') ) x2
       INNER JOIN
       table2 t2
       ON ( x2.COLUMN_VALUE.getStringVal() = t2.col1 );

<强>替代地

您可以使用一个简单的功能:

CREATE TYPE stringlist IS TABLE OF VARCHAR2(4000);
/

CREATE OR REPLACE FUNCTION split_String(
  i_str    IN  VARCHAR2,
  i_delim  IN  VARCHAR2 DEFAULT ','
) RETURN stringlist DETERMINISTIC
AS
  p_result       stringlist := stringlist();
  p_start        NUMBER(5) := 1;
  p_end          NUMBER(5);
  c_len CONSTANT NUMBER(5) := LENGTH( i_str );
  c_ld  CONSTANT NUMBER(5) := LENGTH( i_delim );
BEGIN
  IF c_len > 0 THEN
    p_end := INSTR( i_str, i_delim, p_start );
    WHILE p_end > 0 LOOP
      p_result.EXTEND;
      p_result( p_result.COUNT ) := SUBSTR( i_str, p_start, p_end - p_start );
      p_start := p_end + c_ld;
      p_end := INSTR( i_str, i_delim, p_start );
    END LOOP;
    IF p_start <= c_len + 1 THEN
      p_result.EXTEND;
      p_result( p_result.COUNT ) := SUBSTR( i_str, p_start, c_len - p_start + 1 );
    END IF;
  END IF;
  RETURN p_result;
END;
/

然后你可以这样做:

SELECT t1.id,
       c1.COLUMN_VALUE AS t1_c1,
       t2.col1 AS t2_c1,
       t2.col2 AS t2_c2
FROM   table1 t1
       CROSS JOIN
       TABLE( split_string( t1.col1 ) ) c1
       CROSS JOIN
       TABLE( split_string( t1.col2 ) ) c2
       INNER JOIN
       table2 t2
       ON ( c2.COLUMN_VALUE = t2.col1 )

<强>输出

ID T1_C1 T2_C1 T2_C1
-- ----- ----- -----
 1 PC1   F1    V1   
 1 PC1   F2    V2
 1 PC2   F1    V1
 1 PC2   F2    V2

备选方案2

使用递归子查询分解子句:

WITH bounds ( id, a, b, start_a, end_a, start_b, end_b ) AS (
  SELECT id, col1, col2, 1, INSTR( col1, ',', 1 ), 1, INSTR( col2, ',', 1 )
  FROM   table1
UNION ALL
  SELECT id, a, b,
         end_a + 1,
         INSTR( a, ',', end_a + 1 ),
         CASE end_a WHEN 0 THEN end_b + 1 ELSE start_b END,
         CASE end_a WHEN 0 THEN INSTR( b, ',', end_b + 1 ) ELSE end_b END
  FROM   bounds
  WHERE  end_a > 0 OR end_b > 0
),
data ( id, col1, col2 ) AS (
  SELECT id,
         SUBSTR( a, start_a, CASE end_a WHEN 0 THEN LENGTH(a) + 1 ELSE end_a END - start_a ),
         SUBSTR( b, start_b, CASE end_b WHEN 0 THEN LENGTH(b) + 1 ELSE end_b END - start_b )
  FROM   bounds
)
SELECT d.id,
       d.col1,
       d.col2,
       t.col2
FROM   data d
       INNER JOIN
       table2 t
       ON ( d.col2 = t.col1 )

答案 1 :(得分:0)

这里有几个涉及递归子查询因子的替代方法:

WITH t1 AS (SELECT 1 id, 'PC1,PC2' col1, 'F1,F2' col2 FROM dual UNION ALL
            SELECT 2 id, 'PC1,PC3,PC4' col1, 'F2,F3,F4' col2 FROM dual),
     t2 AS (SELECT 'F1' col1, 'V1' col2 FROM dual UNION ALL
            SELECT 'F2' col1, 'V2' col2 FROM dual UNION ALL
            SELECT 'F3' col1, 'V3' col2 FROM dual UNION ALL
            SELECT 'F4' col1, 'V4' col2 FROM dual),
     -- end of mimicking your tables; see below for the rest of the query you'd need:
     t1_rcrsv1 (ID, col1, split_col1, col2, lvl) AS (SELECT ID, col1, regexp_substr(col1,'[^,]+',1,1), col2, 1 lvl
                                                     FROM   t1
                                                     UNION ALL
                                                     SELECT ID, col1, regexp_substr(col1,'[^,]+',1,lvl+1), col2, lvl + 1
                                                     FROM   t1_rcrsv1
                                                     WHERE  regexp_substr(col1,'[^,]+',1,lvl+1) IS NOT NULL),
     t1_rcrsv2 (ID, col1, col2, split_col2, lvl) AS (SELECT ID, split_col1, col2, regexp_substr(col2,'[^,]+',1,1), 1 lvl
                                                     FROM   t1_rcrsv1
                                                     UNION ALL
                                                     SELECT ID, col1, col2, regexp_substr(col2,'[^,]+',1,lvl+1), lvl + 1
                                                     FROM   t1_rcrsv2
                                                     WHERE  regexp_substr(col2,'[^,]+',1,lvl+1) IS NOT NULL)
SELECT t1a.id,
       t1a.col1 t1_c1,
       t1a.split_col2 t1_c2,
       t2.col2 t2_c2
FROM   t1_rcrsv2 t1a
       INNER JOIN t2 ON t1a.split_col2 = t2.col1
ORDER BY t1a.ID, t1a.col1, t1a.split_col2;

        ID T1_C1       T1_C2    T2_C2
---------- ----------- -------- -----
         1 PC1         F1       V1
         1 PC1         F2       V2
         1 PC2         F1       V1
         1 PC2         F2       V2
         2 PC1         F2       V2
         2 PC1         F3       V3
         2 PC1         F4       V4
         2 PC3         F2       V2
         2 PC3         F3       V3
         2 PC3         F4       V4
         2 PC4         F2       V2
         2 PC4         F3       V3
         2 PC4         F4       V4

首先,上述查询首先循环遍历col1,然后在加入第二个表之前循环遍历col2。但是,你可能会发现在首先循环col2之后更快地进行连接 - 就像这样:

WITH t1 AS (SELECT 1 id, 'PC1,PC2' col1, 'F1,F2' col2 FROM dual UNION ALL
            SELECT 2 id, 'PC1,PC3,PC4' col1, 'F2,F3,F4' col2 FROM dual),
     t2 AS (SELECT 'F1' col1, 'V1' col2 FROM dual UNION ALL
            SELECT 'F2' col1, 'V2' col2 FROM dual UNION ALL
            SELECT 'F3' col1, 'V3' col2 FROM dual UNION ALL
            SELECT 'F4' col1, 'V4' col2 FROM dual),
     -- end of mimicking your tables; see below for the rest of the query you'd need:
     t1_rcrsv1 (ID, col1, col2, split_col2, lvl) AS (SELECT ID, col1, col2, regexp_substr(col2,'[^,]+',1,1), 1 lvl
                                                     FROM   t1
                                                     UNION ALL
                                                     SELECT ID, col1, col2, regexp_substr(col2,'[^,]+',1,lvl+1), lvl + 1
                                                     FROM   t1_rcrsv1
                                                     WHERE  regexp_substr(col2,'[^,]+',1,lvl+1) IS NOT NULL),
     t1_rcrsv2 (ID, col1, split_col1, col2, lvl, t2_c2) AS (SELECT t1a.ID, t1a.col1, regexp_substr(t1a.col1,'[^,]+',1,1), t1a.split_col2, 1 lvl, t2.col2
                                                            FROM   t1_rcrsv1 t1a
                                                                   INNER JOIN t2 ON t1a.split_col2 = t2.col1
                                                            UNION ALL
                                                            SELECT ID, col1, regexp_substr(col1,'[^,]+',1,lvl+1), col2, lvl + 1, t2_c2
                                                            FROM   t1_rcrsv2
                                                            WHERE  regexp_substr(col1,'[^,]+',1,lvl+1) IS NOT NULL)
SELECT id,
       split_col1 t1_c1,
       col2 t1_c2,
       t2_c2
FROM   t1_rcrsv2
ORDER BY ID, t1_c1, t1_c2;

        ID T1_C1       T1_C2    T2_C2
---------- ----------- -------- -----
         1 PC1         F1       V1
         1 PC1         F2       V2
         1 PC2         F1       V1
         1 PC2         F2       V2
         2 PC1         F2       V2
         2 PC1         F3       V3
         2 PC1         F4       V4
         2 PC3         F2       V2
         2 PC3         F3       V3
         2 PC3         F4       V4
         2 PC4         F2       V2
         2 PC4         F3       V3
         2 PC4         F4       V4

您需要测试两个查询(以及MT0提供的解决方案),以查看最适合您的查询。

N.B。如果要返回所有table1的拆分列,无论表2中是否存在匹配,您可能希望将内连接转换为外连接。