如何获取不在 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 中的参考点?的主要内容,如果未能解决你的问题,请参考以下文章
Sql Server函数日期时间函数日期查询今天昨天7天内30天的数据