我正在寻找一个oracle查询,它将转换分隔的列(2列为多行并将其与另一个表连接
id - col1 - col2
1 - PC1,PC2 - F1,F2
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
但这需要花费大量时间才能得出结果。有没有更好的方法来获得结果..?
答案 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中是否存在匹配,您可能希望将内连接转换为外连接。