[英]Grouping to only remove NULLs in SQL
我有一张如下表,里面有房间、时间段,每个时间段都有那个房间里的学生
Room <10:00 10:00 - 14:00 14:00+
101 Sally NULL NULL
101 Jeremy NULL NULL
101 Rick NULL NULL
101 NULL Jeremy NULL
101 NULL Mark NULL
101 NULL NULL Steve
101 NULL NULL Brandon
101 NULL NULL Lily
101 NULL NULL Debbie
201 Nancy NULL NULL
201 NULL Frank NULL
201 NULL NULL Steve
201 NULL NULL Claudette
301 NULL George NULL
301 Jake NULL NULL
所需的输出如下所示:
Room <10:00 10:00 - 14:00 14:00+
101 Sally Jeremy Steve
101 Jeremy Mark Brandon
101 Rick Lily
101 Debbie
201 Nancy Frank Steve
201 Claudette
301 Jake George
随意假设还有更多房间以及每个房间的名称和时间段。 这只是一个小样本。
这里的目标是删除所有 NULL,或者至少减少不需要的地方,并将数据向上推并填充房间中适用的空间。
如果不是因为每个时间段都需要每个学生姓名都有自己的行,我只会使用聚合函数并将它们连接在一起
select
Room
,string_agg('<10:00', '; ') as '<10:00'
,string_agg('10:00 - 14:00', '; ') as '10:00 - 14:00'
,string_agg('14:00+', '; ') as '14:00+'
from room_time_slots
group by room
这将创建这个:
Room <10:00 10:00 - 14:00 14:00+
101 Sally; Jeremy; Rick Jeremy; Mark Steve; Brandon; Lily; Debbie
201 Nancy Frank Steve; Claudette
301 Jake George
但坚持每个学生都必须有自己的线路,所以上述内容不被接受。
我试图创建一种计数器来跟踪每次为房间(每个时间段)指定名称时的情况,然后我将简单地按该计数器分组,这意味着它将从上到下填充表格。 但似乎没有一个 RANK 函数符合我的需要。 我找不到在 SQL 中创建和更新计数器的好方法,因为我的搜索只会让我回到 COUNT 聚合函数,这也不是我需要的。 为此,我需要一个计数器,每次输入一个新名称 PER room PER 时隙时都会增加,然后在遇到新房间时重置为零,我想这在理论上是可行的。
假设每行只填充一个槽,您可以计算一个顺序值,忽略null
值。
然后聚合做你想要的:
select room, max(slot1), max(slot2), max(slot3)
from (select t.*,
(case when slot1 is not null
then row_number() over (partition by room order by (case when slot1 is not null then 1 else 2 end))
when slot2 is not null
then row_number() over (partition by room order by (case when slot2 is not null then 1 else 2 end))
when slot3 is not null
then row_number() over (partition by room order by (case when slot3 is not null then 1 else 2 end))
end) as seqnum
from <tablename> t
) t
where seqnum is not null
group by room, seqnum;
我用更容易输入的内容替换了列名。
我实际上没有太多调用(或机会)来使用完整的外部联接,所以这可能不完全正确,但看起来像这样的事情会起作用:
SELECT q1.room, f1, f2, f3
FROM (SELECT room, f1, ROW_NUMBER() OVER (PARTITION BY room ORDER BY f1) AS rowNum WHERE f1 IS NOT NULL ) AS q1
FULL OUTER JOIN (SELECT room, f2, ROW_NUMBER() OVER (PARTITION BY room ORDER BY f2) AS rowNum WHERE f2 IS NOT NULL ) AS q2 ON q1.room = q2.room AND q1.rowNum = q2.rowNum
FULL OUTER JOIN (SELECT room, f3, ROW_NUMBER() OVER (PARTITION BY room ORDER BY f3) AS rowNum WHERE f3 IS NOT NULL ) AS q2 ON q1.room = q3.room AND q1.rowNum = q3.rowNum
ORDER BY q1.room, q2.room, q3.room
, q1.rowNum, q2.rowNum, q3.rowNum
;
编辑:与戈登的回答类似,将f1
、 f2
和f3
视为实际列名的占位符。
另请注意,这将产生101|Sally|Jeremy|Brandon
而不是101|Sally|Jeremy|Steve
因为尽管您如何向他们展示您的行确实没有固有的顺序。
感谢大家的投入,因为它帮助我朝着我选择的方向前进,并且我使用了您提供的一些元素,因为我无法实现您的任何确切答案。
我最终将元素拆分并为它们分配了自己的位置编号(作为视图,使用 ROW_NUMBER() OVER(PARTITION BY) 等),然后用几个完整的外部联接重新加入它们。
它凌乱且过于复杂,而且非常像撬棍方法,但它完成了工作。 与你们中的许多人建议的基本相似,但为了我的一些轻松而分开并重新加入(虽然绝对乏味)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.