按日期为多行中的每一列选择第一个非空值

时间:2019-02-07 11:49:00

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

我有一个如下表:

+-------------------------+------+------+------+------+  
|          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行)。我担心多个按订单查询可能会降低查询的性能。但是,如果没有更清洁的解决方案出现,我会继续努力。

3 个答案:

答案 0 :(得分:4)

如果您的订单按[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]

答案 1 :(得分:1)

您可以尝试执行以下操作以避免排序。我知道一开始定义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 

答案 2 :(得分:1)

我还没有测试过,但是看起来确实很邪恶:

private void Window_KeyDown(object sender, KeyEventArgs e)
{
        if (e.SystemKey == Key.F10)
        {
            YourLogic(e.SystemKey);
        }

        switch (e.Key)
        {
            case Key.F1:
            case Key.F2:
        }
}

没有窗口功能:

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

Demo on db<>fiddle