【问题标题】:Select first non-null value by date for each column across multiple rows按日期为多行中的每一列选择第一个非空值
【发布时间】:2019-07-01 12:46:48
【问题描述】:

我有一张如下表:

+-------------------------+------+------+------+------+  
|          Date           |  A   |  B   |  C   |  D   |  
+-------------------------+------+------+------+------+  
| 2010-11-16 10:02:00.000 | 10   | NULL | NULL | NULL |  
| 2010-09-21 00:00:00.000 | 86   | 14   | NULL | 17   |  
| 2010-07-27 00:00:00.000 | 125  | 12   | NULL | 11   |  
| 2010-05-29 15:24:00.000 | NULL | NULL | 1250 | NULL |  
+-------------------------+------+------+------+------+  

我需要一个查询来提取每列的第一个非空值。某种“垂直合并”。

期望的结果是:

+-------------------------+------+------+------+------+  
|          Date           |  A   |  B   |  C   |  D   |  
+-------------------------+------+------+------+------+  
| 2010-11-16 10:02:00.000 | 10   | 14   | 1250 | 17   |  
+-------------------------+------+------+------+------+ 

表格按日期排序 (desc)
实际的表有更多的列(40)和行(最多 5000)

编辑:

我的实际表有更多的列和行(假设大约 40 列和多达 5000 行)。我担心多个 order-by 查询可能会降低查询的性能。但是,如果没有更清洁的解决方案出现,我会去的。

【问题讨论】:

    标签: sql sql-server tsql sql-server-2008 null


    【解决方案1】:

    这是一种可能的方法,如果您的订单按[Date] 列递减:

    输入:

    CREATE TABLE #Table (
        [Date] datetime,
        A int,
        B int,
        C int,
        D int
    )
    INSERT INTO #Table
        ([Date], A, B, C, D)
    VALUES
        ('2010-11-16T10:02:00.000', 10  , NULL, NULL, NULL),  
        ('2010-09-21T00:00:00.000', 86  , 14  , NULL, 17  ),  
        ('2010-07-27T00:00:00.000', 125 , 12  , NULL, 11  ),  
        ('2010-05-29T15:24:00.000', NULL, NULL, 1250, NULL)  
    

    声明:

    SELECT 
        [Date] = (SELECT TOP 1 [Date] FROM #Table WHERE [Date] IS NOT NULL ORDER BY [Date] DESC),
        [A] = (SELECT TOP 1 [A] FROM #Table WHERE [A] IS NOT NULL ORDER BY [Date] DESC),
        [B] = (SELECT TOP 1 [B] FROM #Table WHERE [B] IS NOT NULL ORDER BY [Date] DESC),
        [C] = (SELECT TOP 1 [C] FROM #Table WHERE [C] IS NOT NULL ORDER BY [Date] DESC),
        [D] = (SELECT TOP 1 [D] FROM #Table WHERE [D] IS NOT NULL ORDER BY [Date] DESC)
    

    输出:

    Date                    A   B   C       D
    2010-11-16 10:02:00.000 10  14  1250    17
    

    更新 - 另一种可能的方法,使用聚合函数:

    ;WITH DatesCTE AS (
        SELECT
            [Date] = MAX([Date]), 
            [DateA] = MAX(CASE WHEN A IS NOT NULL THEN [Date] END),
            [DateB] = MAX(CASE WHEN B IS NOT NULL THEN [Date] END),
            [DateC] = MAX(CASE WHEN C IS NOT NULL THEN [Date] END),
            [DateD] = MAX(CASE WHEN D IS NOT NULL THEN [Date] END)
        FROM #Table
    )
    SELECT 
        d.[Date],
        A = MAX(CASE WHEN t.[Date] = d.[DateA] THEN A END),
        B = MAX(CASE WHEN t.[Date] = d.[DateB] THEN B END),
        C = MAX(CASE WHEN t.[Date] = d.[DateC] THEN C END),
        D = MAX(CASE WHEN t.[Date] = d.[DateD] THEN D END)
    FROM DatesCTE d
    CROSS APPLY #Table t
    GROUP BY d.[Date]
    

    【讨论】:

    • 这可以工作,遗憾的是我的实际表有更多的列,这个解决方案会产生一个真正丑陋的查询,但是,我正在考虑它,谢谢(问题已编辑)
    • @Jacopo 我已经用另一种使用聚合函数的方法更新了答案。我知道,Salman A 使用了类似的方法,您只需要测试即可。谢谢。
    【解决方案2】:

    您可以尝试这样的方法来避免订购。我知道一开始定义 50 个变量很繁琐,但以后不必担心。

    declare @A int = (select top 1 A from #Table where A is not null)
    declare @B int = (select top 1 B from #Table where B is not null)
    declare @C int = (select top 1 C from #Table where C is not null)
    declare @D int = (select top 1 D from #Table where D is not null)
    
    select top 1 Date,@A,@B,@C,@D
    from #Table 
    

    【讨论】:

      【解决方案3】:

      我没有对此进行测试,但它确实看起来足够邪恶:

      with cte as (
          select date
               , a, max(case when a is not null then date end) over () as date_a
               , b, max(case when b is not null then date end) over () as date_b
               , c, max(case when c is not null then date end) over () as date_c
               , d, max(case when d is not null then date end) over () as date_d
          from t
      )
      select max(date) as date
           , min(case when date = date_a then a end) as a
           , min(case when date = date_b then b end) as b
           , min(case when date = date_c then c end) as c
           , min(case when date = date_d then d end) as d
      from cte
      

      没有窗口函数:

      with cte as (
          select max(case when a is not null then date end) as date_a
               , max(case when b is not null then date end) as date_b
               , max(case when c is not null then date end) as date_c
               , max(case when d is not null then date end) as date_d
          from t
      )
      select max(date) as date
           , min(case when date = date_a then a end) as a
           , min(case when date = date_b then b end) as b
           , min(case when date = date_c then c end) as c
           , min(case when date = date_d then d end) as d
      from t
      cross join cte
      

      Demo on db<>fiddle

      【讨论】:

        猜你喜欢
        • 2022-10-09
        • 1970-01-01
        • 2016-07-02
        • 1970-01-01
        • 1970-01-01
        • 2021-06-18
        • 2015-01-18
        相关资源
        最近更新 更多