Преобразование таблиц / разбор полей в PL / SQL - PullRequest
1 голос
/ 30 апреля 2010

У меня есть нормализованная таблица, что-то вроде

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 ]

4 голосов
/ 02 мая 2010

Другой альтернативой является использование предложения модели:

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

С уважением, Роб.

3 голосов
/ 30 апреля 2010

У меня есть для вас решение на чистом SQL.

Я адаптировал трюк, обнаруженный на старом сайте Ask Tom, опубликованном Михаилом Брату . Моя адаптация использует регулярное выражение для токенизации столбца 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> 
1 голос
/ 30 апреля 2010

Основываясь на книге Селко, вот что я нашел, и она работает хорошо!

  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).

Это основано на том факте, что ЗНАЧЕНИЕ заключено в ',' на обоих концах, например:

,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   
0 голосов
/ 30 апреля 2010

Оригинальный ответ

В SQL Server TSQL мы анализируем строки и создаем объект таблицы. Вот пример кода - может быть, вы можете перевести его.

http://rbgupta.blogspot.com/2007/10/tsql-parsing-delimited-string-into.html

Второй вариант

Подсчитайте количество запятых в строке. Получите максимальное количество запятых. Допустим, во всей таблице есть строка с 5 запятыми максимум. Создайте SELECT с 5 подстрок. Это сделает его основанной на множестве операцией и должен быть намного быстрее, чем rbar.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...