Return 空值之前的前一个值

Return previous value before null value

我目前正在查询缺少索引的 table。

这是一些示例数据:

id  dStartDate
126 2010-04-22 00:00:00.000
127 NULL
128 2010-04-29 00:00:00.000
129 2010-05-03 00:00:00.000
130 NULL
131 NULL
132 NULL
133 2010-04-29 00:00:00.000
134 NULL
135 NULL
136 2010-04-29 00:00:00.000
137 NULL
138 NULL
139 2010-04-29 00:00:00.000
140 NULL
141 2010-04-29 00:00:00.000
142 2010-04-29 00:00:00.000
143 NULL
144 NULL

我使用以下脚本来获取缺失的索引:

declare @id int
declare @maxid int

set @id = 1
select @maxid = max(idJCMaster) from _btblJCMaster

declare @IDseq table    (id int)

while @id < @maxid --whatever you max is
begin
    insert into @IDseq values(@id)

    set @id = @id + 1
end

select 
    s.id
from        @IDseq s 
left join   _btblJCMaster t on s.id = t.idJCMaster
where t.idJCMaster is null

以上工作完美,但是,我想查看以前的记录(不为空)日期,以了解删除该记录的时间...

我将上面的脚本改成了这样:

declare @id int
declare @maxid int

set @id = 1
select @maxid = max(idJCMaster) from _btblJCMaster

declare @IDseq table    (id int)

while @id < @maxid --whatever you max is
begin
    insert into @IDseq values(@id)

    set @id = @id + 1
end

select 
    s.id
,   t.dStartDate
from        @IDseq s 
left join   _btblJCMaster t on s.id = t.idJCMaster

我得到的结果如下所示:

可以看出,对于那些特定的索引,有时会丢失比记录更多的内容...

我不太确定如何更改脚本以显示上一个日期(空之前)。

在这个例子中,我的预期结果是:

请协助预期结果?

非常感谢您的帮助!

编辑

在Ankit的帮助下,尝试了以下(他的回答):

declare @id int
declare @maxid int

set @id = 1
select @maxid = max(idJCMaster) from _btblJCMaster

declare @IDseq table    (id int)

while @id < @maxid --whatever you max is
begin
    insert into @IDseq values(@id)

    set @id = @id + 1
end

select 
    s.id
,   (SELECT MAX(dStartDate)
            FROM _btblJCMaster
            WHERE id >= t1.idJCMaster) dStartDate
from        @IDseq s 
left join   _btblJCMaster t1 on s.id = t1.idJCMaster

但我仍然收到 NULLS

然后我继续尝试他的第一个答案,通过稍微改变 LAG 函数并添加 LEAD,具有 3 个 CTE,但我仍然得到 NULLS:

declare @id int
declare @maxid int

set @id = 1
select @maxid = max(idJCMaster) from _btblJCMaster

declare @IDseq table    (id int)

while @id < @maxid --whatever you max is
begin
    insert into @IDseq values(@id)

    set @id = @id + 1
end


;with cte (id, dStartDate, idJCMaster)
as
(
select 
    s.id
,   ISNULL(dStartDate, isnull(LAG(dStartDate) OVER(order by s.id),LEAD(dStartDate) OVER(order by s.id)))
,   IdJCMaster
from        @IDseq s 
left join   _btblJCMaster t1 on s.id = t1.idJCMaster
)
,   cte2 (id,dStartDate, idJCMaster)
as
(
select
    id
,   isnull(dStartDate,LAG(dStartDate) OVER(order by id))
,   idJCMaster
from    cte
)
,   cte3 (id,dStartDate, idJCMaster)
as
(
select
    id
,   isnull(dStartDate,LEAD(dStartDate) OVER(order by id))
,   idJCMaster
from    cte2
)

select
    id
,   isnull(dStartDate,LAG(dStartDate) OVER(order by id))
from    cte3
where   idJCMaster is null

没有其他更简单的方法来完成这个吗?

您可以尝试以下查询 -

SELECT id, (SELECT MAX(dStartDate)
            FROM YOUR_TABLE
            WHERE id >= t1.id) dStartDate
FROM YOUR_TABLE t1;

你可以试试这个:

首先我们需要一个 模型 table 来模拟您的问题。请在下一个问题中自行提供。始终最好提供一个 self-运行、独立 示例,包括 DDL、INSERT 和您自己的尝试。这样的模拟称为MCVE.

DECLARE @tbl TABLE(id INT,  dStartDate DATE);
INSERT INTO @tbl VALUES
 (126,'2010-04-22 00:00:00.000')
,(127,NULL)
,(128,'2010-04-29 00:00:00.000')
,(129,'2010-05-03 00:00:00.000')
,(130,NULL)
,(131,NULL)
,(132,NULL)
,(133,'2010-04-29 00:00:00.000')
,(134,NULL)
,(135,NULL)
,(136,'2010-04-29 00:00:00.000')
,(137,NULL)
,(138,NULL)
,(139,'2010-04-29 00:00:00.000')
,(140,NULL)
,(141,'2010-04-29 00:00:00.000')
,(142,'2010-04-29 00:00:00.000')
,(143,NULL)
,(144,NULL);

--查询

WITH cte AS(SELECT id,dStartDate FROM @tbl WHERE dStartDate IS NOT NULL)
SELECT t.id 
      ,A.gaplessStartDate
FROM @tbl t
CROSS APPLY(SELECT TOP 1 cte.dStartDate 
            FROM cte 
            WHERE cte.id<=t.id 
            ORDER BY cte.id DESC) A(gaplessStartDate);

简而言之:

我们首先使用 CTE 来获得仅包含非空行的集合。
现在我们可以使用 APPLY 通过调用按降序排序的 较小的 ids 的最顶部来获取合适的行和 id。

这种方法有点像 triangle JOIN (Jeff Moden wrote a great article on this)。任何行都需要一个带有 ORDER BY 操作的相关子查询。

提示:如果您使用索引温度 table 而不是 CTE,则使用更大的集合可能会更快。

感谢@Shnugo 的协助!

在您的帮助下,以下脚本准确地提供了我需要的数据集:

declare @id int
declare @maxid int

set @id = 1
select @maxid = max(idJCMaster) from _btblJCMaster

declare @IDseq table    (id int)

while @id < @maxid --whatever you max is
begin
    insert into @IDseq values(@id)

    set @id = @id + 1
end

;with source (id,dStartDate)
as
(
select 
    s.id
,   dStartDate
from        @IDseq s 
left join   _btblJCMaster t1 on s.id = t1.idJCMaster
)
, cte AS(SELECT id,dStartDate FROM source WHERE dStartDate IS NOT NULL)
SELECT t.id 
      ,A.gaplessStartDate
FROM source t
CROSS APPLY(SELECT TOP 1 cte.dStartDate 
            FROM cte 
            WHERE cte.id<=t.id 
            ORDER BY cte.id DESC) A(gaplessStartDate)
WHERE t.dStartDate IS NULL
order by id

这仅供其他观众使用,如果您需要的话。