Sql 按特殊条件筛选日期范围
我有一个表,其中包含按StartDate排序的以下数据:Sql 按特殊条件筛选日期范围,sql,sql-server,tsql,intervals,date-range,Sql,Sql Server,Tsql,Intervals,Date Range,我有一个表,其中包含按StartDate排序的以下数据: | Id | StartDate | EndDate | |----|---------------------|---------------------| | 1 | 2017-03-18 00:00:00 | 2017-03-18 02:00:00 | | 2 | 2017-03-18 01:30:00 | 2017-03-18 04:00:00 |
| Id | StartDate | EndDate |
|----|---------------------|---------------------|
| 1 | 2017-03-18 00:00:00 | 2017-03-18 02:00:00 |
| 2 | 2017-03-18 01:30:00 | 2017-03-18 04:00:00 |
| 3 | 2017-03-18 01:45:00 | 2017-03-18 03:00:00 |
| 4 | 2017-03-18 02:10:00 | 2017-03-18 02:30:00 |
| 5 | 2017-03-18 02:35:00 | 2017-03-18 02:50:00 |
| 6 | 2017-03-18 03:20:00 | 2017-03-18 03:50:00 |
| 7 | 2017-03-18 05:00:00 | 2017-03-18 05:30:00 |
| 8 | 2017-03-18 05:10:00 | 2017-03-18 07:00:00 |
| 9 | 2017-03-18 05:50:00 | 2017-03-18 08:00:00 |
过滤逻辑:
在第一段日期之后,我们发现另一段日期与之不相交。
然后,针对所发现的周期重复该逻辑
过滤后应保留:
| Id | StartDate | EndDate |
|----|---------------------|---------------------|
| 1 | 2017-03-18 00:00:00 | 2017-03-18 02:00:00 |
| 4 | 2017-03-18 02:10:00 | 2017-03-18 02:30:00 |
| 5 | 2017-03-18 02:35:00 | 2017-03-18 02:50:00 |
| 6 | 2017-03-18 03:20:00 | 2017-03-18 03:50:00 |
| 7 | 2017-03-18 05:00:00 | 2017-03-18 05:30:00 |
| 9 | 2017-03-18 05:50:00 | 2017-03-18 08:00:00 |
是否可以在不使用光标的情况下实现此功能
为方便使用记录:
CREATE TABLE #Dates (Id INT, StartDate DATETIME, EndDate DATETIME);
INSERT INTO #Dates SELECT 1, '2017-03-18 00:00:00', '2017-03-18 02:00:00';
INSERT INTO #Dates SELECT 2, '2017-03-18 01:30:00', '2017-03-18 04:00:00';
INSERT INTO #Dates SELECT 3, '2017-03-18 01:45:00', '2017-03-18 03:00:00';
INSERT INTO #Dates SELECT 4, '2017-03-18 02:10:00', '2017-03-18 02:30:00';
INSERT INTO #Dates SELECT 5, '2017-03-18 02:35:00', '2017-03-18 02:50:00';
INSERT INTO #Dates SELECT 6, '2017-03-18 03:20:00', '2017-03-18 03:50:00';
INSERT INTO #Dates SELECT 7, '2017-03-18 05:00:00', '2017-03-18 05:30:00';
INSERT INTO #Dates SELECT 8, '2017-03-18 05:10:00', '2017-03-18 07:00:00';
INSERT INTO #Dates SELECT 9, '2017-03-18 05:50:00', '2017-03-18 08:00:00';
DROP TABLE #Dates;
是的,您可以在没有光标的情况下执行此操作。以下是使用exists的版本,假设在开始日期没有完全相同的版本:
select min(id), min(startdate), max(startdate)
from (select t.*, sum(flag) over (order by startdate) as grp
from (select t.*,
(case when not exists (select 1
from t t2
where t2.startdate < t.startdate and
t2.enddate >= t.startdate
)
then 1 else 0
end) as flag
from t
) t
) t
group by grp;
最里面的子查询计算一个标志以确定行是否重叠。中间层累积此标志以标识每个组,外部层进行聚合。从SQL Server 2012开始,您可以使用和函数来实现此目的:
CREATE TABLE #Dates (Id INT, StartDate DATETIME, EndDate DATETIME);
INSERT INTO #Dates SELECT 1, '2017-03-18 00:00:00', '2017-03-18 02:00:00';
INSERT INTO #Dates SELECT 2, '2017-03-18 01:30:00', '2017-03-18 04:00:00';
INSERT INTO #Dates SELECT 3, '2017-03-18 01:45:00', '2017-03-18 03:00:00';
INSERT INTO #Dates SELECT 4, '2017-03-18 02:10:00', '2017-03-18 02:30:00';
INSERT INTO #Dates SELECT 5, '2017-03-18 02:35:00', '2017-03-18 02:50:00';
INSERT INTO #Dates SELECT 6, '2017-03-18 03:20:00', '2017-03-18 03:50:00';
INSERT INTO #Dates SELECT 7, '2017-03-18 05:00:00', '2017-03-18 05:30:00';
INSERT INTO #Dates SELECT 8, '2017-03-18 05:10:00', '2017-03-18 07:00:00';
INSERT INTO #Dates SELECT 9, '2017-03-18 05:50:00', '2017-03-18 08:00:00';
SELECT T1.Id, T1.StartDate, T1.EndDate FROM (
SELECT *,
LAG(EndDate) OVER(ORDER BY ID) as lagdate ,
LEAD(StartDate) OVER(ORDER BY ID) as leaddate
FROM #Dates) AS T1
WHERE T1.StartDate > T1.lagdate OR T1.lagdate IS NULL
OR T1.EndDate < T1.leaddate OR T1.leaddate IS NULL
这就是结果:
注意:T1.lagdate为NULL用于获取第一行,T1.leaddate为NULL用于获取最后一行
/*这是过滤逻辑的代码:在第一段日期之后,我们找到另一段不相交的日期。然后,针对所发现的周期重复该逻辑*/
WHILE(@@ROWCOUNT > 0) -- we did something the last insert
BEGIN
INSERT INTO @Results
SELECT TOP 1 *
FROM #Dates d
WHERE d.StartDate =
(SELECT MIN(StartDate)
FROM #Dates
END
SELECT *
FROM @Results
ORDER BY 1
DROP TABLE #Dates;
Id StartDate EndDate
1 2017-03-18 00:00:00.000 2017-03-18 02:00:00.000
4 2017-03-18 02:10:00.000 2017-03-18 02:30:00.000
5 2017-03-18 02:35:00.000 2017-03-18 02:50:00.000
6 2017-03-18 03:20:00.000 2017-03-18 03:50:00.000
7 2017-03-18 05:00:00.000 2017-03-18 05:30:00.000
9 2017-03-18 05:50:00.000 2017-03-18 08:00:00.000
找到的解决方案:
您使用的sql server版本是什么?我使用MSSQL server 2014您可以使用LAG函数来实现这一点,更多信息请查看我的答案details@IvanAntonov感谢您输入示例数据-使每个人都能更轻松地使用。抱歉,解决方案无法正常工作。这项任务比看起来要困难得多。您将在1、5、6、7中显示Id为的记录。@IvanAntonov等待您的反馈:非常感谢您的帮助!但是,此解决方案仅适用于当前输入数据,如果您稍微更改输入数据,该解决方案将产生错误的结果:使用此数据:插入日期选择1,'2017-03-18 04:00:00','2017-03-18 10:30:00';插入日期选择2,“2017-03-18 05:00:00”,“2017-03-18 15:00:00”;插入日期选择3,“2017-03-18 07:00:00”,“2017-03-18 20:00:00”;在日期中插入选择4,“2017-03-18 11:00:00”,“2017-03-18 23:00:00”;在日期中插入选择5,‘2017-03-18 12:30:00’、‘2017-03-18 19:00:00’;您可以选择Id为1、5的记录,但按照逻辑,应该获得Id为1、5的记录4@IvanAntonov很高兴你的问题解决了。幸运的是,我描述问题的本质是错误的。目前,不存在的解决方案应该输出第二个表中的记录。但是,这个解决方案不能正常工作,我看不到修复它的可能性:。@IvanAntonov。我认为问题仅仅在于,不存在的国家正在寻找时间框架中的任何重叠。但是,只有在开始重叠时才需要设置标志。干得好。祝你好运
WHILE(@@ROWCOUNT > 0) -- we did something the last insert
BEGIN
INSERT INTO @Results
SELECT TOP 1 *
FROM #Dates d
WHERE d.StartDate =
(SELECT MIN(StartDate)
FROM #Dates
END
SELECT *
FROM @Results
ORDER BY 1
DROP TABLE #Dates;
Id StartDate EndDate
1 2017-03-18 00:00:00.000 2017-03-18 02:00:00.000
4 2017-03-18 02:10:00.000 2017-03-18 02:30:00.000
5 2017-03-18 02:35:00.000 2017-03-18 02:50:00.000
6 2017-03-18 03:20:00.000 2017-03-18 03:50:00.000
7 2017-03-18 05:00:00.000 2017-03-18 05:30:00.000
9 2017-03-18 05:50:00.000 2017-03-18 08:00:00.000
WITH PIP AS (
SELECT [D1].*, [T].*
FROM #Dates [D1]
OUTER APPLY (
SELECT TOP 1 Id AS NextId FROM #Dates [D2]
WHERE
NOT ([D1].StartDate <= [D2].EndDate AND [D1].EndDate >= [D2].StartDate) AND [D2].Id > [D1].Id
ORDER BY [D2].StartDate
) AS [T]
), POP AS (
SELECT [T].Id, [T].StartDate, [T].EndDate, [T].NextId
FROM PIP [T]
WHERE [T].Id = 1
UNION ALL
SELECT [X].Id, [X].StartDate, [X].EndDate, [X].NextId
FROM PIP [X]
JOIN POP [H] ON [H].NextId = [X].Id
)
SELECT * FROM POP;