Свернуть несколько записей в одну запись с несколькими столбцами - PullRequest
0 голосов
/ 07 февраля 2011

В поддерживаемой мною программе заказчик получил массивное (~ 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  

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

Обновление:

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

Ответы [ 2 ]

0 голосов
/ 08 февраля 2011

Звучит так, как будто вы ищете поворот .

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;

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

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

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

0 голосов
/ 07 февраля 2011

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

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;

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

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