我想填写需要开始和结束日期信息的日历对象。我有一列包含一系列日期。有些日期是连续的(有一天差异),有些则没有。
InfoDate
2013-12-04 consecutive date [StartDate]
2013-12-05 consecutive date
2013-12-06 consecutive date [EndDate]
2013-12-09 [startDate]
2013-12-10 [EndDate]
2014-01-01 [startDate]
2014-01-02
2014-01-03 [EndDate]
2014-01-06 [startDate]
2014-01-07 [EndDate]
2014-01-29 [startDate]
2014-01-30
2014-01-31 [EndDate]
2014-02-03 [startDate]
2014-02-04 [EndDate]
我想选择每个连续日期范围的开始和结束日期(块中的第一个和最后一个)。
StartDate EndDate
2013-12-04 2013-12-06
2013-12-09 2013-12-10
2014-01-01 2014-01-03
2014-01-06 2014-01-07
2014-01-29 2014-01-31
2014-02-03 2014-02-04
我想仅使用SQL来解决问题。
答案 0 :(得分:44)
不需要连接或递归CTE。标准的gap-and-island解决方案是分组(值减去row_number),因为它在连续序列中是不变的。开始日期和结束日期只是组的MIN()和MAX()。
WITH t AS (
SELECT InfoDate d,ROW_NUMBER() OVER(ORDER BY InfoDate) i
FROM @d
GROUP BY InfoDate
)
SELECT MIN(d),MAX(d)
FROM t
GROUP BY DATEDIFF(day,i,d)
答案 1 :(得分:4)
你去..
;WITH CTEDATES
AS
(
SELECT ROW_NUMBER() OVER (ORDER BY Infodate asc ) AS ROWNUMBER,infodate FROM YourTableName
),
CTEDATES1
AS
(
SELECT ROWNUMBER, infodate, 1 as groupid FROM CTEDATES WHERE ROWNUMBER=1
UNION ALL
SELECT a.ROWNUMBER, a.infodate,case datediff(d, b.infodate,a.infodate) when 1 then b.groupid else b.groupid+1 end as gap FROM CTEDATES A INNER JOIN CTEDATES1 B ON A.ROWNUMBER-1 = B.ROWNUMBER
)
select min(mydate) as startdate, max(infodate) as enddate from CTEDATES1 group by groupid
如果这回答了你的问题,请不要忘记将其标记为答案。
答案 2 :(得分:0)
我已将这些值插入名为#consec
的表中,然后执行以下操作:
select t1.*
,t2.infodate as binfod
into #temp1
from #consec t1
left join #consec t2 on dateadd(DAY,1,t1.infodate)=t2.infodate
select t1.*
,t2.infodate as binfod
into #temp2
from #consec t1
left join #consec t2 on dateadd(DAY,1,t2.infodate)=t1.infodate
;with cte as(
select infodate, ROW_NUMBER() over(order by infodate asc) as seq from #temp1
where binfod is null
),
cte2 as(
select infodate, ROW_NUMBER() over(order by infodate asc) as seq from #temp2
where binfod is null
)
select t2.infodate as [start_date]
,t1.infodate as [end_date] from cte t1
left join cte2 t2 on t1.seq=t2.seq
只要您的日期不重叠,那就应该为您完成工作。
答案 3 :(得分:0)
这是我的测试数据样本:
--required output
-- 01 - 03
-- 08 - 09
-- 12 - 14
DECLARE @maxRN int;
WITH #tmp AS (
SELECT CAST('2013-01-01' AS date) DT
UNION ALL SELECT CAST('2013-01-02' AS date)
UNION ALL SELECT CAST('2013-01-03' AS date)
UNION ALL SELECT CAST('2013-01-05' AS date)
UNION ALL SELECT CAST('2013-01-08' AS date)
UNION ALL SELECT CAST('2013-01-09' AS date)
UNION ALL SELECT CAST('2013-01-12' AS date)
UNION ALL SELECT CAST('2013-01-13' AS date)
UNION ALL SELECT CAST('2013-01-14' AS date)
),
#numbered AS (
SELECT 0 RN, CAST('1900-01-01' AS date) DT
UNION ALL
SELECT ROW_NUMBER() OVER (ORDER BY DT) RN, DT
FROM #tmp
)
SELECT * INTO #tmpTable FROM #numbered;
SELECT @maxRN = MAX(RN) FROM #tmpTable;
INSERT INTO #tmpTable
SELECT @maxRN + 1, CAST('2100-01-01' AS date);
WITH #paired AS (
SELECT
ROW_NUMBER() OVER(ORDER BY TStart.DT) RN, TStart.DT DTS, TEnd.DT DTE
FROM #tmpTable TStart
INNER JOIN #tmpTable TEnd
ON TStart.RN = TEnd.RN - 1
AND DATEDIFF(dd,TStart.DT,TEnd.DT) > 1
)
SELECT TS.DTE, TE.DTs
FROM #paired TS
INNER JOIN #paired TE ON TS.RN = TE.RN -1
AND TS.DTE <> TE.DTs -- you could remove this filter if you want to have start and end on the same date
DROP TABLE #tmpTable
将#tmp数据替换为您的实际表格。
答案 4 :(得分:0)
您可以这样做,这里是sqlfiddle
select
min(ndate) as start_date,
max(ndate) as end_date
from
(select
ndate,
dateadd(day, -row_number() over (order by ndate), ndate) as rnk
from dates
) t
group by
rnk
答案 5 :(得分:0)
另一个可以在此处使用的简单解决方案是-
with tmp as
(
select
datefield
, dateadd('day',-row_number() over(order by date asc),datefield) as date_group
from table
)
select
min(datefield) as start_date
, max(datefield) as end_date
from tmp
group by date_group
答案 6 :(得分:0)
create table mysql.date_test ( name VARCHAR(10) , d date);
insert into mysql.date_test values
('Bill','2021-04-01'),
('Bill','2021-04-02'),
('Bill','2021-04-03'),
('Bill','2021-04-05'),
('Bill','2021-04-06'),
('Bill','2021-04-07'),
('Bill','2021-04-09'),
('Bill','2021-04-10');
Subtract row_number from the date. You will get the same date if dates are conscutive.
mysql> select name,d,row_number() over (partition by name order by d) as rn,ADDDATE(d,INTERVAL - row_number() over (partition by name order by d) DAY) as grp
-> from mysql.date_test;
+------+------------+----+------------+
| name | d | rn | grp |
+------+------------+----+------------+
| Bill | 2021-04-01 | 1 | 2021-03-31 |
| Bill | 2021-04-02 | 2 | 2021-03-31 |
| Bill | 2021-04-03 | 3 | 2021-03-31 |
| Bill | 2021-04-05 | 4 | 2021-04-01 |
| Bill | 2021-04-06 | 5 | 2021-04-01 |
| Bill | 2021-04-07 | 6 | 2021-04-01 |
| Bill | 2021-04-09 | 7 | 2021-04-02 |
+------+------------+----+------------+
7 rows in set (0.00 sec)
Then group on the static date
mysql> select name,min(d) as start_date,max(d) as end_date
-> from
-> (
-> select name,d,row_number() over (partition by name order by d),ADDDATE(d,INTERVAL - row_number() over (partition by name order by d) DAY) as grp
-> from mysql.date_test
-> )a
-> group by name,grp;
+------+------------+------------+
| name | start_date | end_date |
+------+------------+------------+
| Bill | 2021-04-01 | 2021-04-03 |
| Bill | 2021-04-05 | 2021-04-07 |
| Bill | 2021-04-09 | 2021-04-10 |
+------+------------+------------+
答案 7 :(得分:-1)
SELECT InfoDate ,
CASE
WHEN TRUNC(InfoDate - 1) = TRUNC(lag(InfoDate,1,InfoDate) over (order by InfoDate))
THEN NULL
ELSE InfoDate
END STARTDATE,
CASE
WHEN TRUNC(InfoDate + 1) = TRUNC(lead(InfoDate,1,InfoDate) over (order by InfoDate))
THEN NULL
ELSE InfoDate
END ENDDATE
FROM TABLE;