【问题标题】: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]

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

【问题讨论】:

    标签: sql sql-server tsql sql-server-2012 greatest-n-per-group


    【解决方案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

    【讨论】:

      猜你喜欢
      • 2013-04-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-06-28
      • 2021-05-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多