PL / SQL中的表转换/字段解析

时间:2010-04-30 13:03:45

标签: sql oracle plsql

我已经对表格进行了非规范化处理,例如

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?

明显的解决方案是通过游标实现它。有什么想法吗?

4 个答案:

答案 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快得多。