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

Dam*_*ker 4 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`
Run Code Online (Sandbox Code Playgroud)

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

输出应该是 -

Email           Name1   Name2   Job
Test1@test.com  Ron     Smith   NULL
Test2@test.com  NULL    Ford    Plumber
Run Code Online (Sandbox Code Playgroud)

我已经编写了一些相当丑陋的 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]
Run Code Online (Sandbox Code Playgroud)

如果这有帮助的话,这是示例表的 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')
Run Code Online (Sandbox Code Playgroud)

Gor*_*off 6

有一些方法不需要那么多连接。没有一个是简单的,因为 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;
Run Code Online (Sandbox Code Playgroud)

另一种方法是使用外部应用:

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 . . .
Run Code Online (Sandbox Code Playgroud)