如何在每列中选择下一个非空值。 T-SQL

时间:2016-04-05 13:59:39

标签: sql sql-server sql-server-2012

我试图根据日期值选择每列的最后一个非空值。

我有一张看起来像这样的表 -

Email           Name1   Name2   Job     Date
Test1@test.com  Ron     NULL    NULL    2015-01-01 00:00:00.000
Test1@test.com  Dave    Smith   NULL    2014-01-01 00:00:00.000
Test1@test.com  NULL    NULL    NULL    2013-01-01 00:00:00.000
Test2@test.com  NULL    Smith   NULL    2014-01-01 00:00:00.000
Test2@test.com  NULL    Ford    Plumber 2015-01-01 00:00:00.000`

我想为每个电子邮件地址显示每列的最新非空值。

输出应为 -

Email           Name1   Name2   Job
Test1@test.com  Ron     Smith   NULL
Test2@test.com  NULL    Ford    Plumber

我已经编写了一些非常难看的SQL来解决这个问题但是我想将这个逻辑应用到另一个包含更多列的表中。

我的问题是 - 是否有更简单的方法来执行此操作而无需为每列加入?

目前的解决方案如下 -

select distinct  a.[Email],b.[Name1],c.[Name2],d.[job] from 
(
select [Email] from #test
)
A
left join 
(
SELECT [Email],
 FIRST_VALUE([Name1]) over(partition by [Email] order by [Date] desc) as [Name1]
from #test
where [Name1] is not null
) b
on a.[Email] = b.[Email]
left join 
(
SELECT [Email],
FIRST_VALUE([Name2]) over(partition by [Email] order by [Date] desc) as [Name2]
from  #test
where [Name2] is not null
) c
on a.[Email] = c.[Email]
left join 
(
select [Email],
FIRST_VALUE([Job]) over(partition by [Email] order by [Date] desc) as [Job]
from #test
where  [Job] is not null
) d
on a.[Email] = d.[Email]

如果有帮助的话,这里是示例表的DDL / DML -

create table #test
([Email] nvarchar(50),
[Name1]  nvarchar(50),
[Name2] nvarchar(50),
[Job] nvarchar(50),
[Date] datetime)

insert into #test
values
('Test1@test.com', 'Ron', null,null,'20150101'),
('Test1@test.com', 'Dave' ,'Smith',null, '20140101'),
('Test1@test.com', null, null, null ,'20130101'),
('Test2@test.com', null, 'Smith', null, '20140101'),
('Test2@test.com', null, 'Ford', 'Plumber','20150101')

2 个答案:

答案 0 :(得分:3)

有些方法不需要这么多连接。没有一个是简单的,因为SQL Server不支持ignore nulls上的lag()选项。

基本上,您需要在每列上执行逻辑操作。没有子查询的一种方法是:

select distinct email,
       first_value(name1) over (partition by email
                                order by (case when name1 is not null then date else '2000-01-01' end) desc
                               ) as name1,
       . . .
from #test;

另一种选择使用外部申请:

select t.email, name1, . . .
from (select distinct email from #test t) t outer apply
     (select top 1 name1
      from #test t2
      where t2.email = t.email and name1 is not null
      order by date desc
     ) name1 . . .

答案 1 :(得分:2)

您可以将FIRST_VALUEDISTINCT

一起使用
SELECT DISTINCT Email, 
       FIRST_VALUE(Name1) OVER (PARTITION BY Email 
                                ORDER BY CASE 
                                           WHEN Name1 IS NULL THEN '19000101' 
                                           ELSE [Date] 
                                         END DESC) AS Name1,
       FIRST_VALUE(Name2) OVER (PARTITION BY Email 
                                ORDER BY CASE 
                                           WHEN Name2 IS NULL THEN '19000101' 
                                           ELSE [Date] 
                                         END DESC) AS Name2,
       FIRST_VALUE(Job) OVER (PARTITION BY Email 
                                ORDER BY CASE 
                                           WHEN Job IS NULL THEN '19000101' 
                                           ELSE [Date] 
                                         END DESC) AS Job
FROM test