将多个记录折叠为具有多个列的单个记录

时间:2011-02-07 17:38:41

标签: sql db2

在我正在维护的程序中,客户给出了大量(~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 :(得分: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;

不能保证它有效,但希望它会有所帮助。另一种方法是使用自定义聚合函数。

答案 1 :(得分: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。