Oracle - 将一行或多行连接到不同的结果列
Posted
技术标签:
【中文标题】Oracle - 将一行或多行连接到不同的结果列【英文标题】:Oracle - concat one or more rows to different result columns 【发布时间】:2021-10-14 11:08:25 【问题描述】:我有一个包含如下数据的表结构:
EventNbr | NoteNbr | NoteText
1 1 Example title
1 2 text1
1 3 text2
2 4 Example title 2
3 5 Example title 3
3 6 text3
因此我需要的是一个看起来像这样的数据集
EventNbr | Title | Notes
1 Example Title text1,text2
2 Example Title2
3 Example Title3 text3
我基本上从每个EventNbr
中取出最小的NoteNbr
并将其放在Title
列中,然后在MIN
之后的每个其他NoteNbr
将与@987654328 中的逗号连接在一起@专栏。
我目前拥有的作品,但仅适用于具有多个 NoteNbr
行的 EventNbr
s。它不适用于只有 one NoteNbr
行的项目,例如上面的 EventNbr
2。
SELECT A.EventNbr,
MIN(A.NoteText) AS Title,
LISTAGG(A.NoteText, ',') WITHIN GROUP(ORDER BY A.NoteNbr) AS Notes
FROM EventNote A
INNER JOIN (SELECT Min(NoteNbr) Min_NoteNbr, EventNbr
FROM EventNote
GROUP BY EventNbr) B
ON (A.NoteNbr <> B.Min_NoteNbr AND A.EventNbr = B.EventNbr)
INNER JOIN EventNote C
ON (C.NoteNbr = B.Min_NoteNbr AND C.EventNbr = B.EventNbr)
GROUP BY A.EventNbr;
结果:
EventNbr | Title | Notes
1 Example Title text1,text2
3 Example Title3 text3
我需要添加什么来考虑只有一个NoteNbr
行的情况?
【问题讨论】:
有些可疑。你说你有一个表,但在你的查询中你有两个表的连接。是哪个? @mathguy 抱歉打错了 - 已修复 【参考方案1】:可以使用条件聚合和row_number()
:
select eventnbr,
max(case when seqnum = 1 then notetext end) as title,
listagg(case when seqnum > 1 then notetext end, ',') within group (order by seqnum) as notes
from (select en.*,
row_number() over (partition by eventnbr order by notenbr) as seqnum
from eventnote en
) en
group by eventnbr;
【讨论】:
【参考方案2】:最好先运行聚合。这将产生几乎你需要的结果,除了它仍然会在笔记的开头连接标题。这可以在事后纠正,使用标准字符串函数substr
、instr
和连接。 (在没有实际注释的情况下,需要后者来处理您提到的“特殊情况”。)
优点是附加操作仅在输出上执行 - 预计(远?)比输入少的行,并且附加操作是微不足道的字符串操作,而不是额外的排序和分区层。
这样的事情 - 假设输入都在一个表中(正如您问题的第一部分所暗示的那样),而不是在不同的表中(如您现有的代码所暗示的那样)。我包含了一个with
子句来模拟输入表。
注意——执行计划表明优化器足够聪明,可以将子查询合并到主查询中;该计划由对聚合 (GROUP BY
) 的单个 SELECT
操作组成。 all_notes
在子查询中被替换为 listagg
的长定义,完全消除了外部查询。因此,我们拥有两全其美的优势:一个可以读取的查询,但执行效率尽可能高。
with
eventnote(eventnbr, notenbr, notetext) as (
select 1, 1, 'Example title' from dual union all
select 1, 2, 'text1' from dual union all
select 1, 3, 'text2' from dual union all
select 2, 4, 'Example title 2' from dual union all
select 3, 5, 'Example title 3' from dual union all
select 3, 6, 'text3' from dual
)
select eventnbr, title,
substr(all_notes, instr(all_notes || ',', ',') + 1) as notes
from (
select eventnbr,
min(notetext) keep (dense_rank first order by notenbr) as title,
listagg(notetext, ',') within group (order by notenbr) as all_notes
from eventnote
group by eventnbr
)
order by eventnbr
;
EVENTNBR TITLE NOTES
-------- --------------- ------------------------------
1 Example title text1,text2
2 Example title 2
3 Example title 3 text3
【讨论】:
以上是关于Oracle - 将一行或多行连接到不同的结果列的主要内容,如果未能解决你的问题,请参考以下文章