2011-02-07 4 views
0

В программе, которую я поддерживаю, мы получили заказчик массивным (~ 500 строк) SQL-заявлением. Он используется для создания плоских файлов с фиксированными записями длины для передачи данных другому крупному бизнесу. Поскольку его массивный плоский файл не реляционный, а стандартные нормальные формы данных рушится. Итак, если у вас есть запись, которая может содержать несколько кодов, в этом случае до 19, все они записываются в одну строку, а отдельные поля - в плоском файле.Свернуть несколько записей в одну запись с несколькими столбцами

Примечание: этот пример упрощен.

Данные могут выглядеть следующим образом, с тремя столами:

RECORDS 
record_id firstname lastname  
--------------------------------  
123   Bob   Schmidt 
324   George  Washington 
325   Ronald  Reagan 
290   George  Clooney 


CODE_TABLE 
code_id  code_cd  code_txt 
-------------------------------- 
5   3   President 
2   4   Actor  
3   7   Plumber 


CODES_FOR_RECORDS 
record_id code_cd 
------------------- 
123   7  
325   3 
290   4 
324   3 
325   4 
123   4 

Это необходимо производить записи, как:

firstname lastname code1  code2  code3 
Bob   Schmidt  Actor  Plumber  NULL 
George  Washington President NULL  NULL 
Ronald  Reagon  Actor  President NULL 
George  Clooney  Actor  NULL  NULL 

Часть текущего запроса мы были даны, выглядит так, но с 19 кодовыми столбцами вместо 5:

select 
    x.record_id, 
    max(case when x.rankk = 1 then code_txt end) as CodeColumn1, 
    max(case when x.rankk = 2 then code_txt end) as CodeColumn2, 
    max(case when x.rankk = 3 then code_txt end) as CodeColumn3, 
    max(case when x.rankk = 4 then code_txt end) as CodeColumn4, 
    max(case when x.rankk = 5 then code_txt end) as CodeColumn5, 
from 
    (
     select 
      r.record_id, 
      ct.code_txt as ctag , 
      dense_rank() over (partition by r.record_id order by cfr.code_id) as rankk 
     from    
      records as r 
      codes_for_records as cfr, 
      code_table as ct 
     where 
      r.record_id = cfr.record_id 
      and ct.code_cd = cfr.code_cd 
      and cfr.code_cd is not null 
      and ct.code_txt not like '%V%' 
    ) as x 
where 
    x.record_id is not null 
group by 
    x.record_id 

I trimm но фактические статуты включают в себя внутренний запрос и соединение и многое другое, где условия, но это должно получить эту идею. Мой мозг говорит мне, что должен быть лучший способ, но я не эксперт по SQL. Мы используем DB2 v8, если это помогает. И коды должны быть в отдельных столбцах, поэтому не объединяются вещи в одну строку. Есть ли более чистое решение, чем это?

Update:

Я в конечном итоге просто refacorting исходный запрос, он использует подоконник уродливую MAX() бизнес, но в целом запрос гораздо более удобным для чтения из-за переделки других частей.

+0

Когда я пишу код для преобразования данных вручную, процедура выполняется редко до 1000 строк. Этот код выглядит довольно простым и понятным для меня. – HLGEM

+0

Возможно, это то, что я не очень хорошо знаком с SQL, потому что для меня это кажется запутанным. И часть этого - 1000 строк, когда разделение на логические куски не плохо. 500 строк SQL, которые так взаимосвязаны, похоже, спагетти - это еще одна вещь, ИМХО. – troutinator

ответ

0

Похоже, что вы ищете pivoting.

WITH joined_table(firstname, lastname, code_txt, rankk) AS 
(
SELECT 
    r.firstname, 
    r.lastname, 
    ct.code_txt, 
    dense_rank() over (partition by r.record_id order by cfr.code_id) as rankk 
FROM 
    records r 
INNER JOIN 
    codes_for_records cfr 
    ON r.record_id = cfr.record_id 
INNER JOIN 
    codes_table ct 
    ON ct.code_cd = cfr.code_cd 
), 

decoded_table(firstname, lastname, 
    CodeColumn1, CodeColumn2, CodeColumn3, CodeColumn4, CodeColumn5) AS 
(
    SELECT 
    firstname, 
    lastname, 
    DECODE(rankk, 1, code_txt), 
    DECODE(rankk, 2, code_txt), 
    DECODE(rankk, 3, code_txt), 
    DECODE(rankk, 4, code_txt), 
    DECODE(rankk, 5, code_txt) 
    FROM 
    joined_table jt 
) 

SELECT 
    firstname, 
    lastname, 
    MAX(CodeColumn1), 
    MAX(CodeColumn2), 
    MAX(CodeColumn3), 
    MAX(CodeColumn4), 
    MAX(CodeColumn5) 
FROM 
    decoded_table dt 
GROUP BY 
    firstname, 
    lastname; 

Обратите внимание, что я никогда не делал этого раньше. Я полагаюсь на linked document в качестве ссылки.

Возможно, вам потребуется включить record_id для учета дубликатов имен.

Редактировать: Добавлено GROUP BY.

+0

Интересно. Я сделаю это. – troutinator

+0

Я все еще думаю, что все это «MAX()» дело уродливое. – troutinator

0

Одним из возможных решений является использование рекурсивного запроса:

with recursive_view (record_id, rankk, final) as 
(
    select 
    record_id, 
    rankk, 
    cast (ctag as varchar (100)) 
    from inner_query t1 

    union all 

    select 
    t1.record_id, 
    t1.rankk, 
    /* all formatting here */ 
    cast (t2.final || ',' || t1.ctag as varchar (100)) 
    from 
    inner_query t1, 
    recursive_view t2 
    where 
    t2.rankk < t1.rankk 
    and t1.record_id = t2.record_id 
    and locate(t1.ctag, t2.final) = 0 
) 
select record_id, final from recursive_view; 

не может гарантировать, что это работает, но надеюсь, что это будет полезно. Другим способом является использование настраиваемой агрегированной функции.

+0

Спасибо за предложения. Однако каждый код должен быть оставлен как столбец. Он не может быть соединен с одной гигантской струной. – troutinator

+0

Он может быть отформатирован путем заполнения, чтобы выглядеть как куча столбцов. В противном случае без сводной и динамической sql вы вынуждены использовать двойные max (case) конструкции. –

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