SQL查询:获取@to后首个时间戳,无匹配则默认@to
按ID分组查询指定时间后的首个时间戳,无匹配返回默认值
表结构与示例数据
假设有一张包含Id、Value、TimeStamp字段的表(TimeStamp为datetime类型,示例采用伪时间格式),数据如下:
| ID | Value | TimeStamp |
|---|---|---|
| 101 | 10 | '1-feb-2020' |
| 101 | 20 | '28-feb-2020' |
| 202 | 5 | '15-feb-2020' |
| 202 | 15 | '20-feb-2020' |
| 303 | 50 | '10-feb-2020' |
| 303 | 60 | '1-mar-2020' |
需求
按Id分组,查询每个Id在指定时间戳@to之后的第一条记录的TimeStamp;若不存在符合条件的记录,则默认返回@to(或null)。
背景
需要获取@from与@to时间段内各时间间隔(时、日、月、年)的首个和最后一个Value,以及@from之前、@to之后的首个Value。
尝试过的SQL语句
DECLARE @to DateTime = '28-feb-2020' SELECT Id, MIN( TimeStamp ) FirstTimeStampAfterToOrDefault FROM MyTable WHERE Id IN ( 101,202, 303 ) AND TimeStamp >= @to GROUP BY Id
存在的问题
这段SQL的问题在于,当某个Id没有大于等于@to的TimeStamp记录时,该Id不会出现在结果集中,无法返回默认的@to值。
期望查询结果
| Id | FirstTimeStampAfterToOrDefault |
|---|---|
| 101 | '28-feb-2020' (等于@to) |
| 202 | '28-feb-2020' (默认@to) |
| 303 | '1-mar-2020' (存在大于@to的时间戳) |
解决方案
要保证所有目标Id都出现在结果里,同时替换无匹配的情况为@to,可以用以下两种方法:
方法1:手动指定目标ID列表
如果需要查询的Id是固定的,先通过CTE生成这些Id的列表,再用左关联查询匹配记录:
DECLARE @to DateTime = '28-feb-2020' WITH TargetIds AS ( SELECT 101 AS Id UNION ALL SELECT 202 UNION ALL SELECT 303 ) SELECT t.Id, COALESCE(MIN(m.TimeStamp), @to) AS FirstTimeStampAfterToOrDefault FROM TargetIds t LEFT JOIN MyTable m ON t.Id = m.Id AND m.TimeStamp >= @to GROUP BY t.Id
方法2:查询原表所有存在的ID
如果需要覆盖原表中所有的Id,可以先获取原表去重后的Id列表,再关联查询:
DECLARE @to DateTime = '28-feb-2020' WITH AllIds AS ( SELECT DISTINCT Id FROM MyTable ) SELECT a.Id, COALESCE(MIN(m.TimeStamp), @to) AS FirstTimeStampAfterToOrDefault FROM AllIds a LEFT JOIN MyTable m ON a.Id = m.Id AND m.TimeStamp >= @to GROUP BY a.Id
关键说明
LEFT JOIN确保所有目标Id都会出现在结果集里,哪怕没有符合条件的时间戳记录COALESCE会把无匹配时MIN(m.TimeStamp)返回的null值替换成@toMIN(m.TimeStamp)用于筛选出符合条件的最早时间戳,也就是我们要的第一条记录的时间
内容的提问来源于stack exchange,提问作者Newnamtab
相关产品推荐
相关产品推荐

