2010-04-30 1 views
1

Я де-нормированный стол, что-то вродеТаблица преобразования/поля синтаксического анализа в PL/SQL

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

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

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 вариантов для этого типа запроса в этом BlogPost: http://rwijk.blogspot.com/2007/11/interval-based-row-generation.html

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

+0

Слайк-решение Rob. Я считаю, что «модель» трудно понять, поэтому я никогда не обращу внимания на первое. Хорошее сообщение в блоге тоже. – APC

+0

+1 и принято - очень круто! Это более общее решение, чем у моего – IMHO

+0

. Это работает для оракула 9? –

3

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

Я адаптировал трюк, который я нашел на an old Ask Tom site, posted by Mihail Bratu. В моей адаптации используется регулярное выражение, чтобы обозначить столбец VALUE, поэтому требуется 10 г или выше.

Данные испытаний.

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> 
+0

Я думаю о создании предложения «WITH» для этого ... – IMHO

+0

Я получаю странные ошибки, когда я пытаюсь запустить это; вы уверены, что «как sys.dbms_debug_vc2coll»? – IMHO

+0

sys.dbms_debug_vc2coll - это тип, определенный как «таблица varchar2 (1000)». –

0

Оригинал ответа

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

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

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

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

+0

хорошо, я знаю, как это сделать - проблема в том, что это очень медленно, так как он должен делать запись уровня обработки – IMHO

+0

второй вариант не будет работать. Возможно, у меня есть 20 или 30 токенов Однако, я думаю, что нашел решение в книге Селко, похожее на то, что предлагает ur. Я буду тестировать и размещать решение, если оно работает – IMHO

1

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

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

http://en.wikipedia.org/wiki/Joe_Celko – IMHO

Смежные вопросы