繁体   English   中英

分组以仅删除 SQL 中的 NULL

[英]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
;

编辑:与戈登的回答类似,将f1f2f3视为实际列名的占位符。

另请注意,这将产生101|Sally|Jeremy|Brandon而不是101|Sally|Jeremy|Steve因为尽管您如何向他们展示您的行确实没有固有的顺序。

感谢大家的投入,因为它帮助我朝着我选择的方向前进,并且我使用了您提供的一些元素,因为我无法实现您的任何确切答案。

我最终将元素拆分并为它们分配了自己的位置编号(作为视图,使用 ROW_NUMBER() OVER(PARTITION BY) 等),然后用几个完整的外部联接重新加入它们。

它凌乱且过于复杂,而且非常像撬棍方法,但它完成了工作。 与你们中的许多人建议的基本相似,但为了我的一些轻松而分开并重新加入(虽然绝对乏味)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM