如何跨多列和多行“汇总”数据

Posted

技术标签:

【中文标题】如何跨多列和多行“汇总”数据【英文标题】:How to "Roll-Up" data across multiple columns and rows 【发布时间】:2020-09-08 12:43:05 【问题描述】:

我有一个审核表,我们在其中记录对数据库中字段的更改。我有一个查询,我能够从审计中获取关于几列、它们记录的更改以及何时与适用的 ID 相关联的数据子集。以下是输出的示例:

ID      ada       IsHD  HDF   DTStamp
-----------------------------------------------------
68      NULL      0     0     2020-04-28 21:12:21.287
68      NULL      NULL  NULL  2020-04-17 14:59:49.700
68      No/Unsure NULL  NULL  2020-04-17 14:03:46.160
68      NULL      0     0     2020-04-17 13:49:49.720
102     NULL      NULL  NULL  2020-04-30 13:11:15.273
102     No/Unsure NULL  NULL  2020-04-20 16:00:35.410
102     NULL      1     1     2020-04-20 15:59:55.750
105     No/Unsure 1     1     2020-04-17 12:06:10.833
105     NULL      NULL  NULL  2020-04-13 07:51:30.180
126     NULL      NULL  NULL  2020-05-01 17:59:24.460
126     NULL      0     0     2020-04-28 21:12:21.287

我想弄清楚的是最有效的方法是“汇总”给定 ID 的多行,以便保留最新的非 NULL 值,只为该 ID 留下一行。

也就是转这个:

68      NULL      0     0     2020-04-28 21:12:21.287
68      NULL      NULL  NULL  2020-04-17 14:59:49.700
68      No/Unsure NULL  NULL  2020-04-17 14:03:46.160
68      NULL      0     0     2020-04-17 13:49:49.720
102     NULL      NULL  NULL  2020-04-30 13:11:15.273
102     No/Unsure NULL  NULL  2020-04-20 16:00:35.410
102     NULL      1     1     2020-04-20 15:59:55.750

进入这个:

68      No/Unsure 0     0     2020-04-28 21:12:21.287
102     No/Unsure 1     1     2020-04-30 13:11:15.273

...等等。这几乎就像你要按下结果的顶部并挤出所有的 NULL 一样。

将上述结果转储到临时表@audit 然后我运行以下查询:

SELECT DISTINCT a.[ID]
     , (SELECT TOP 1 [ADA]
        FROM @audit
        WHERE [ID] = a.[ID]
          AND [ADA] IS NOT NULL
        ORDER BY [DTStamp] DESC) AS 'ADA'
     , (SELECT TOP 1 [IsHD]
        FROM @audit
        WHERE [ID] = a.[ID]
          AND [IsHD] IS NOT NULL
        ORDER BY [DTStamp] DESC) AS 'IsHD'
     , (SELECT TOP 1 [HDF]
        FROM @audit
        WHERE [ID] = a.[ID]
          AND [HDF] IS NOT NULL
        ORDER BY [DTStamp] DESC) AS 'HDF'
     , (SELECT Max([DTStamp])
        FROM @audit
        WHERE [ID] = a.[ID]) AS 'DTStamp'
FROM @audit a
ORDER BY [ID]

这是我想出的方法,它确实有效,但感觉非常笨拙且效率低下。有没有更好的方法来实现最终目标?

【问题讨论】:

【参考方案1】:

如果您希望每个 id 一行,则使用聚合:

select id, max(ada), max(IsHD), max(HDF), max(DTStamp)
from @audit a
group by id;

这适用于您提供的数据,并且似乎符合您想要的规则。

【讨论】:

非常感谢,这很好用。我曾想过这个问题,但我不相信自己会这么简单。去搞清楚。 :) 在将您的示例付诸实施后,我发现这种逻辑不成立的情况,因为我认为我首先有疑问。我提到我需要“最新的非 NULL 值”,但是您给出的示例获取了最大值,对于字符串来说,最大值是字母表中较高的值,INT 成为最大的数字......我需要 newest i> 或 最近的 不只是 Max。我更新了我的查询以更清楚地说明这一需求。【参考方案2】:

我了解您希望每列的每个 id 的“最新”非空值,使用列 DTStamp 进行排序。

您使用多个子查询的方法可以满足您的需求。另一种方法是使用多个row_number()s 和条件聚合。这可能实际上更有效,因为它避免了对表的多次扫描。

select
    id,
    max(case when rn_ada  = 1 then ada  end) ada,
    max(case when rn_isHd = 1 then isHd end) isHd,
    max(case when rn_hdf  = 1 then hdf  end) hdf,
    max(DTStamp) DTStamp
from (
    select 
        a.*,
        row_number() over(
            partition by id
            order by case when ada is not null then DTStamp end desc
        ) rn_ada,
        row_number() over(
            partition by id
            order by case when isHd is not null then DTStamp end desc
        ) rn_isHd,
        row_number() over(
            partition by id
            order by case when hdf is not null then DTStamp end desc
        ) rn_hdf
    from @audit a
) t
group by id
order by id

Demo on DB Fiddle

编号 |阿达 |是高清 |高清晰度电视 | DTStamp --: | :-------- | ---: | --: | :------------------------ 68 |否/不确定 | 0 | 0 | 2020-04-28 21:12:21.287 102 |否/不确定 | 1 | 1 | 2020-04-30 13:11:15.273

【讨论】:

以上是关于如何跨多列和多行“汇总”数据的主要内容,如果未能解决你的问题,请参考以下文章

安卓网格布局如何用java代码实现跨多行跨多列

大数据集群跨多版本升级业务0中断,只因背后有TA

Angular项目间的模板共享:如何跨多项目重用公共HTML

R Data.table 用于计算跨多列的汇总统计信息

Xamarin 表单:显示和绑定具有多行和多列的列表/网格的数据

如何在 J2me 中创建具有多行和多列的列表?