Sql 将多个记录折叠为具有多列的单个记录
在我维护的一个程序中,客户给了我们一个500行左右的SQL语句。它用于生成具有固定长度记录的平面文件,以便将数据传输到另一家大企业。因为它是一个巨大的平面文件,所以它不是关系型的,标准的标准数据格式也被压缩了。因此,如果您有一个记录可以关联多个代码,在本例中最多19个,那么它们都已写入平面文件中的单行但单独的字段中 注:此示例已简化 数据可能如下所示,有三个表:Sql 将多个记录折叠为具有多列的单个记录,sql,db2,Sql,Db2,在我维护的一个程序中,客户给了我们一个500行左右的SQL语句。它用于生成具有固定长度记录的平面文件,以便将数据传输到另一家大企业。因为它是一个巨大的平面文件,所以它不是关系型的,标准的标准数据格式也被压缩了。因此,如果您有一个记录可以关联多个代码,在本例中最多19个,那么它们都已写入平面文件中的单行但单独的字段中 注:此示例已简化 数据可能如下所示,有三个表: RECORDS record_id firstname lastname ----------------------
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
为了简单起见,我删减了一些内容,但实际的语句包括一个内部查询和一个连接以及更多的where条件,但这应该能让人明白这一点。我的大脑告诉我必须有更好的方法,但我不是SQL专家。如果有帮助的话,我们正在使用DB2V8。代码必须在单独的列中,因此不能将内容合并到单个字符串中。有比这更干净的解决方案吗
更新:
我最终只是重新编写了原始查询,它使用了丑陋的MAX business,但由于重新编写了其他部分,因此总体上查询的可读性要高得多 可能的解决方案之一是使用递归查询:
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;
我不能保证它能工作,但希望它能有所帮助。另一种方法是使用自定义聚合函数。听起来您正在寻找的是 请注意,我以前从未亲自做过这件事。我把这本书当作参考书 您可能需要包含记录id以说明重复的名称
编辑:添加分组依据。当我编写代码手动转换数据时,该过程很少少于1000行。这段代码对我来说非常简单和直接,也许是因为我对SQL不太熟悉,因为对我来说这似乎有点复杂。它的一部分是1000行,当分成逻辑块时,这并不坏。500行SQL是如此相互关联,好像意大利面是另一回事,IMHO。谢谢你的建议。但是,每个代码都需要保留为其on collumn。它不能连接成一个巨大的字符串。它可以通过填充来格式化,看起来像一堆列。否则,如果没有pivot和DynamicSQL,您将被迫使用重复的maxcase构造。我试试看。我还是觉得麦克斯的生意很难看。
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;