如何获取不在 30 天内的下一个最小日期并用作 SQL 中的参考点?

Posted

技术标签:

【中文标题】如何获取不在 30 天内的下一个最小日期并用作 SQL 中的参考点?【英文标题】:How to get next minimum date that is not within 30 days and use as reference point in SQL? 【发布时间】:2016-08-16 18:47:08 【问题描述】:

我有一个如下所示的记录子集:

ID DATE
A  2015-09-01
A  2015-10-03
A  2015-10-10
B  2015-09-01
B  2015-09-10
B  2015-10-03
...

对于每个 ID,第一个最小日期是第一个索引记录。现在我需要排除索引记录后 30 天内的病例,任何日期大于 30 天的记录都将成为另一个索引记录。

例如,对于 ID A,2015-09-01 和 2015-10-03 都是索引记录,并且将被保留,因为它们相隔超过 30 天。 2015-10-10 将被删除,因为它在第二个索引案例的 30 天内。

对于 ID B,2015-09-10 将被删除并且不会成为索引案例,因为它在第一个索引记录的 30 天内。 2015-10-03 将被保留,因为它超过了第一个索引记录的 30 天,将被视为第二个索引案例。

输出应如下所示:

ID DATE
A  2015-09-01
A  2015-10-03
B  2015-09-01
B  2015-10-03

如何在 SQL Server 2012 中执行此操作?一个 ID 可以有多少个日期没有限制,可能只有 1 个到 5 个或更多。我对 SQL 相当基础,因此我们将不胜感激。

【问题讨论】:

我们还面临这个问题吗? 【参考方案1】:

试试这个解决方案。

Sample demo

with diffs as (
select t1.id,t1.dt strtdt,t2.dt enddt,datediff(dd,t1.dt,t2.dt) daysdiff
from t t1
join t t2 on t1.id=t2.id and t1.dt<t2.dt
)
, y as (
select id,strtdt,enddt
from (
select id,strtdt,enddt,row_number() over(partition by id,strtdt order by daysdiff) as rn
from diffs
where daysdiff > 30
) x 
where rn=1
)
,z as (
select *,coalesce(lag(enddt) over(partition by id order by strtdt),strtdt) prevend
from y)
select id,strtdt from z where strtdt=prevend
union
select id,enddt from z where strtdt=prevend

【讨论】:

【参考方案2】:

您在问题中解释的逻辑是错误的,在一个地方,您说取第一个索引记录,然后在下一个地方考虑​​立即记录..

这适用于即时记录:

with cte
as
(
select *, ROW_NUMBER() over (partition by id order by datee) as rownum
from #test
)
select *,datediff(day,beforedate,datee)
from cte t1
cross apply
(Select isnull(max(Datee),t1.datee) as beforedate from cte t2 where t1.id =t2.id and  t2.rownum<t1.rownum) b
where datediff(day,beforedate,datee)= 0 or datediff(day,beforedate,datee)>=30

这适用于恒定的基本记录:

select *,datediff(day,basedate,datee) from #test t1
cross apply
(select min(Datee) as basedate from #test t2 where t1.id=t2.id)b
where datediff(day,basedate,datee)>=30 or datediff(day,basedate,datee)=0

【讨论】:

@downvoters:请评论为什么这被否决【参考方案3】:
    select * from 
   (
    select ID,DATE_, case when DATE_DIFF is null then 1 when date_diff>30 then 1 else 0 end comparison from
        (
          select ID, DATE_ ,DATE_-LAG(DATE_, 1) OVER (PARTITION BY ID ORDER BY DATE_)  date_diff from trial
        )
    )
    where comparison=1 order by ID,DATE_;

在 Oracle 数据库中尝试过。 SQL Server 中也有类似的功能。

我按 Id 列分组,并根据 DATE 字段,将当前字段中的日期与其前一个字段进行比较。给定用户 ID 的第一行将返回 null,并且我们的输出中需要第一个字段作为第一个索引。对于所有其他字段,当与前一个字段的日期差大于 30 时,我们返回 1。

Lag function in transact sql

Case function in transact sql

【讨论】:

SQL Server 中别名之前是否必须使用“AS”?在这种情况下 ORDER BY DATE_) AS date_diff 对于 SQL server : select * from ( select ID,DATE_, case when DATE_DIFF is null then 1 when date_diff>30 then 1 else 0 end AS comparison from (select ID, DATE_ ,DATE_-LAG( DATE_, 1) OVER (PARTITION BY ID ORDER BY DATE_) AS date_diff from trial ) ) where comparison=1 order by ID,DATE_; 谢谢,我试过了,但在 ')' 附近出现语法错误。问我问题的另一种方式是,如何仅选择每个 ID 相隔 >=30 天的记录?同时忽略或删除其间的所有内容。 试过注释中的代码了吗?我提到了别名差异@KChan 上面的代码做你想做的事。 :-) 确定问题是否仍然存在。【参考方案4】:

像你的例子一样工作,#test 是你的数据表:

;with cte1
as
(
    select 
        ID, Date, 
        row_number()over(partition by ID order by Date) groupID
    from #test
),
cte2
as
(
    select ID, Date, Date as DateTmp, groupID, 1 as getRow from cte1 where groupID=1
    union all
    select 
        c1.ID, 
        c1.Date, 
        case when datediff(Day, c2.DateTmp, c1.Date) > 30 then c1.Date else c2.DateTmp end as DateTmp,
        c1.groupID, 
        case when datediff(Day, c2.DateTmp, c1.Date) > 30 then 1 else 0 end as getRow
    from cte1 c1
    inner join cte2 c2 on c2.groupID+1=c1.groupID and c2.ID=c1.ID
)
select ID, Date from cte2 where getRow=1 order by ID, Date

【讨论】:

以上是关于如何获取不在 30 天内的下一个最小日期并用作 SQL 中的参考点?的主要内容,如果未能解决你的问题,请参考以下文章

审查超过 3 个月和最近 30 天内的脚本

如何使用c#计算两天内的日期时间? [复制]

Sql Server函数日期时间函数日期查询今天昨天7天内30天的数据

获取七天内的日期和整点时间

Sql Server日期查询-SQL查询今天昨天7天内30天(转)

JavaScript获取当天到14天内的日期及星期几