【问题标题】:Return previous value before null value返回空值之前的前一个值
【发布时间】:2019-11-21 06:36:47
【问题描述】:

我目前正在查询一个缺少索引的表。

这是一些示例数据:

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

我得到的结果是这样的:

可以看出,有时这些特定索引的缺失会超过记录...

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

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

请协助预期结果?

非常感谢您的帮助!

编辑

在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

没有其他更简单的方法可以做到这一点吗?

【问题讨论】:

    标签: tsql sql-server-2017


    【解决方案1】:

    你可以试试这个:

    首先我们需要一个样机表来模拟您的问题。请在下一个问题中自己提供。最好提供一个自运行的独立示例,包括 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 操作的相关子查询。

    提示:如果您使用索引临时表而不是 CTE,则使用较大的集合可能会更快。

    【讨论】:

    • 谢谢@Shnugo!关于最好提供一个自运行的独立示例,包括 DDL、INSERT 和您自己的尝试我下次一定会提供它:-) 这个脚本就像一个魅力,但是,我可以请求更改以仅显示 NULL 的 ID,这样,您只能看到那些不存在的日期吗?如果你明白我的意思吗?
    • @Birel 如果我理解正确,添加WHERE t.dStartDate IS NULL 作为最后一行就足够了。
    【解决方案2】:

    您可以尝试以下查询-

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

    【讨论】:

    • 谢谢 Ankit,我运行它并收到这条消息:The function 'LAG' must have an OVER clause with ORDER BY. 然后我添加了这样的订单:ISNULL(dStartDate, LAG(dStartDate) OVER(order by s.id)),它现在正在工作。但是,由于一次丢失 3 条以上的记录,一些记录仍然显示为空......无论如何都围绕这个?
    • 哦。我忘记了那个原因。让我也纠正一下。
    • @Birel,请立即尝试。
    • Ankit,试过你正在编辑 - 没有成功,请查看我对我的问题所做的编辑...
    • @AnkitBajpai 如果您仔细查看样本数据,您会发现 id 不断增加,但日期却没有。使用MAX(dStartdate) 不会导致需要的结果...
    【解决方案3】:

    感谢@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
    

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-02-20
      • 2020-08-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多