我已经对表格进行了非规范化处理,例如
CODES
ID | VALUE
10 | A,B,C
11 | A,B
12 | A,B,C,D,E,F
13 | R,T,D,W,W,W,W,W,S,S
转换的工作是来自VALUE的每个令牌将生成新行的位置。例如:
CODES_TRANS
ID | VALUE_TRANS
10 | A
10 | B
10 | C
11 | A
11 | B
在不使用自定义pl / sql包的情况下在PL / SQL中执行此操作的最佳方法是什么,理想情况下使用纯SQL?
明显的解决方案是通过游标实现它。有什么想法吗?
答案 0 :(得分:4)
另一种选择是使用model子句:
SQL> select id
2 , value
3 from codes
4 model
5 return updated rows
6 partition by (id)
7 dimension by (-1 i)
8 measures (value)
9 ( value[for i from 0 to length(value[-1])-length(replace(value[-1],',')) increment 1]
10 = regexp_substr(value[-1],'[^,]+',1,cv(i)+1)
11 )
12 order by id
13 , i
14 /
ID VALUE
---------- -------------------
10 A
10 B
10 C
11 A
11 B
12 A
12 B
12 C
12 D
12 E
12 F
13 R
13 T
13 D
13 W
13 W
13 W
13 W
13 W
13 S
13 S
21 rows selected.
在此博文中,我已为此类查询编写了多达6种替代方案:http://rwijk.blogspot.com/2007/11/interval-based-row-generation.html
此致 罗布。
答案 1 :(得分:3)
我有一个纯粹的SQL解决方案。
我调整了我在an old Ask Tom site, posted by Mihail Bratu找到的技巧。我的改编使用正则表达式来标记VALUE列,因此它需要10g或更高。
测试数据。
SQL> select * from t34
2 /
ID VALUE
---------- -------------------------
10 A,B,C
11 A,B
12 A,B,C,D,E,F
13 R,T,D,W1,W2,W3,W4,W5,S,S
SQL>
查询:
SQL> select t34.id
2 , t.column_value value
3 from t34
4 , table(cast(multiset(
5 select regexp_substr (t34.value, '[^(,)]+', 1, level)
6 from dual
7 connect by level <= length(value)
8 ) as sys.dbms_debug_vc2coll )) t
9 where t.column_value != ','
10 /
ID VALUE
---------- -------------------------
10 A
10 B
10 C
11 A
11 B
12 A
12 B
12 C
12 D
12 E
12 F
13 R
13 T
13 D
13 W1
13 W2
13 W3
13 W4
13 W5
13 S
13 S
21 rows selected.
SQL>
答案 2 :(得分:1)
根据Celko的书,这是我发现的,它运作良好!
SELECT
TABLE1.ID
, MAX(SEQ1.SEQ) AS START_POS
, SEQ2.SEQ AS END_POS
, COUNT(SEQ2.SEQ) AS PLACE
FROM
TABLE1, V_SEQ SEQ1, V_SEQ SEQ2
WHERE
SUBSTR(',' || TABLE1.VALUE || ',', SEQ1.SEQ, 1) = ','
AND SUBSTR(',' || TABLE1.VALUE || ',', SEQ2.SEQ, 1) = ','
AND SEQ1.SEQ < SEQ2.SEQ
AND SEQ2.SEQ <= LENGTH(TABLE1.VALUE)
GROUP BY TABLE1.ID, TABLE1.VALUE, SEQ2.SEQ
其中V_SEQ是一个包含一个字段的静态表:
SEQ, integer values 1 through N, where N >= MAX_LENGTH(VALUE).
这是基于VALUE两端用','包裹的事实,如下所示:
,A,B,C,D,
如果您的令牌是固定长度的(就像我的情况一样),我只使用PLACE字段来计算实际的字符串。如果是可变长度,请使用start_pos和end_pos
所以,在我的情况下,令牌是2个字符长,所以最终的SQL是:
SELECT
TABLE1.ID
, SUBSTR(TABLE1.VALUE, T_SUB.PLACE * 3 - 2 , 2 ) AS SINGLE_VAL
FROM
(
SELECT
TABLE1.ID
, MAX(SEQ1.SEQ) AS START_POS
, SEQ2.SEQ AS END_POS
, COUNT(SEQ2.SEQ) AS PLACE
FROM
TABLE1, V_SEQ SEQ1, V_SEQ SEQ2
WHERE
SUBSTR(',' || TABLE1.VALUE || ',', SEQ1.SEQ, 1) = ','
AND SUBSTR(',' || TABLE1.VALUE || ',', SEQ2.SEQ, 1) = ','
AND SEQ1.SEQ < SEQ2.SEQ
AND SEQ2.SEQ <= LENGTH(TABLE1.VALUE)
GROUP BY TABLE1.ID, TABLE1.VALUE, SEQ2.SEQ
) T_SUB
INNER JOIN
TABLE1 ON TABLE1.ID = T_SUB.ID
ORDER BY TABLE1.ID, T_SUB.PLACE
答案 3 :(得分:0)
原始答案
在SQL Server TSQL中,我们解析字符串并创建表对象。这是示例代码 - 也许您可以翻译它。
http://rbgupta.blogspot.com/2007/10/tsql-parsing-delimited-string-into.html
第二个选项
计算每行的逗号数。获取最大逗号数。假设在整个表中你有一行最多5个逗号。用5个子串构建一个SELECT。这将使它成为一个基于集合的操作,并且应该比rbar快得多。