Limitation of SQL Server recursive query - sql
I have this data structure
PresentationSession: 1 day has 2 sessions: morning=1, afternoon=2. Some presentation is the whole day presentation, some only has morning or afternoon.
dayofweek column represents the day of the week for the presentation-eg Monday=2, Tues=3...
Maximum session is the maximum of consecutive sessions a presentation last. Presentation has to be confined within a multiple of a week. For example, if the presentation take place in Wed, Th, Fr and the max Session is 12 then the presentation last 2 weeks. (1 day has 2 sessions). However, if there is a holiday in says Thu, then it is still 2 weeks but 10 sessions, hence the MaxSession is still 12. On the other side of the spectrum, if the MaxSession is only 2, then there will be 3 different presentation on that week.
Newblock marks the start of the week, 0= new start of the week.
rn is the sequential number of the day
My goal is to aggregate the records and consolidate the information to a more human readable format with fromDate, endDate, presentationId, Name (see illustration)
The following recursive cte script does the job just fine but it only works with a data set of 50 records. It will error out with a data set of 2000+ records.
The error is:
Msg 530, Level 16, State 1, Line 110
The statement terminated. The maximum recursion 100 has been exhausted before statement completion.
At first, I thought Oracle had the same issue but further checking shows that the recursion limitation is isolated to SQL Server only!! The exact same data set works in Oracle like a charm.
Below is the "working" cte statement in SQL Server (up to 50 records)
with r (PresentationDate, Presentationid,Name,MaxSession,PresentationDay,rn,blocknum,pos,lastmaxrow) AS
(
select PresentationDate, Presentationid, Name, MaxSession, PresentationDay, rn,1,1,MaxSession
from presentation
where rn=1
union all
select u.PresentationDate, u.Presentationid, u.Name, u.MaxSession, u.PresentationDay, u.rn,
case
when r.pos=r.lastmaxrow or u.newblock=0 then
r.blocknum+1
else r.blocknum
end,
case
when r.pos = r.lastmaxrow or u.newblock=0 then 1
else r.pos+1
end,
case
when r.pos = r.lastmaxrow or u.newblock=0 then r.lastmaxrow
else r.MaxSession
end
from presentation u
join r
on u.Presentationid=r.Presentationid and u.rn=r.rn+1
)
select min(r.PresentationDate) as fromDate, max(r.PresentationDate) as todate,
r.Presentationid, r.Name
from r
group by r.Presentationid, r.Name, r.blocknum
order by r.Name, r.blocknum
So my questions are:
1) Is there something in my "working" code that could be improved to bypass the recursive limitation in SQL Server?
2) Is there a config switch in SQL Server that I can flip to allow recursive more than 100 times?
3) If the answer for the 2 above questions is no, is it possible to convert the recursive statement to non recursive statement without using cursor? I understand I may need multiple statements with other temporary table, but if the use of cursor is not necessary, I prefer not using it.
I have a full test sample in excel if needed. Since I do not know if I can upload it here, I attached an extract of my sample hopefully it is large enough to illustrate my point.
PresentationDate,PresentationDay,PresentationSession,PresentationID,Name,DayOfWeek,MaxSession,rn,newblock
2016-05-24 00:00:00.000,3,1,ID1,ABC,3,8,1,0
2016-05-24 00:00:00.000,3,2,ID1,ABC,3,8,2,1
2016-05-25 00:00:00.000,4,1,ID1,ABC,4,8,3,1
2016-05-25 00:00:00.000,4,2,ID1,ABC,4,8,4,1
2016-05-26 00:00:00.000,5,1,ID1,ABC,5,8,5,1
2016-05-26 00:00:00.000,5,2,ID1,ABC,5,8,6,1
2016-05-27 00:00:00.000,6,1,ID1,ABC,6,8,7,1
2016-05-27 00:00:00.000,6,2,ID1,ABC,6,8,8,1
2016-05-31 00:00:00.000,3,1,ID1,ABC,3,8,9,0
2016-05-31 00:00:00.000,3,2,ID1,ABC,3,8,10,1
2016-06-01 00:00:00.000,4,1,ID1,ABC,4,8,11,1
2016-06-01 00:00:00.000,4,2,ID1,ABC,4,8,12,1
2016-06-02 00:00:00.000,5,1,ID1,ABC,5,8,13,1
2016-06-02 00:00:00.000,5,2,ID1,ABC,5,8,14,1
2016-06-03 00:00:00.000,6,1,ID1,ABC,6,8,15,1
2016-06-03 00:00:00.000,6,2,ID1,ABC,6,8,16,1
2016-06-07 00:00:00.000,3,1,ID1,ABC,3,8,17,0
2016-06-07 00:00:00.000,3,2,ID1,ABC,3,8,18,1
2016-06-08 00:00:00.000,4,1,ID1,ABC,4,8,19,1
2016-06-08 00:00:00.000,4,2,ID1,ABC,4,8,20,1
2016-06-09 00:00:00.000,5,1,ID1,ABC,5,8,21,1
2016-06-09 00:00:00.000,5,2,ID1,ABC,5,8,22,1
2016-06-10 00:00:00.000,6,1,ID1,ABC,6,8,23,1
2016-06-10 00:00:00.000,6,2,ID1,ABC,6,8,24,1
2016-06-14 00:00:00.000,3,1,ID1,ABC,3,8,25,0
2016-06-14 00:00:00.000,3,2,ID1,ABC,3,8,26,1
2016-06-15 00:00:00.000,4,1,ID1,ABC,4,8,27,1
2016-06-15 00:00:00.000,4,2,ID1,ABC,4,8,28,1
2016-06-16 00:00:00.000,5,1,ID1,ABC,5,8,29,1
2016-06-16 00:00:00.000,5,2,ID1,ABC,5,8,30,1
2016-06-17 00:00:00.000,6,1,ID1,ABC,6,8,31,1
2016-06-17 00:00:00.000,6,2,ID1,ABC,6,8,32,1
2016-06-21 00:00:00.000,3,1,ID1,ABC,3,8,33,0
2016-06-21 00:00:00.000,3,2,ID1,ABC,3,8,34,1
2016-06-22 00:00:00.000,4,1,ID1,ABC,4,8,35,1
2016-06-22 00:00:00.000,4,2,ID1,ABC,4,8,36,1
2016-06-23 00:00:00.000,5,1,ID1,ABC,5,8,37,1
2016-06-23 00:00:00.000,5,2,ID1,ABC,5,8,38,1
2016-06-24 00:00:00.000,6,1,ID1,ABC,6,8,39,1
2016-06-24 00:00:00.000,6,2,ID1,ABC,6,8,40,1
2016-06-28 00:00:00.000,3,1,ID1,ABC,3,8,41,0
2016-06-28 00:00:00.000,3,2,ID1,ABC,3,8,42,1
2016-06-29 00:00:00.000,4,1,ID1,ABC,4,8,43,1
2016-06-29 00:00:00.000,4,2,ID1,ABC,4,8,44,1
2016-06-30 00:00:00.000,5,1,ID1,ABC,5,8,45,1
2016-06-30 00:00:00.000,5,2,ID1,ABC,5,8,46,1
2016-07-01 00:00:00.000,6,1,ID1,ABC,6,8,47,1
2016-07-01 00:00:00.000,6,2,ID1,ABC,6,8,48,1
2016-07-05 00:00:00.000,3,1,ID1,ABC,3,8,49,0
2016-07-05 00:00:00.000,3,2,ID1,ABC,3,8,50,1
2016-07-06 00:00:00.000,4,1,ID1,ABC,4,8,51,1
2016-07-06 00:00:00.000,4,2,ID1,ABC,4,8,52,1
2016-07-07 00:00:00.000,5,1,ID1,ABC,5,8,53,1
2016-07-07 00:00:00.000,5,2,ID1,ABC,5,8,54,1
2016-07-08 00:00:00.000,6,1,ID1,ABC,6,8,55,1
2016-07-08 00:00:00.000,6,2,ID1,ABC,6,8,56,1
2016-07-12 00:00:00.000,3,1,ID1,ABC,3,8,57,0
2016-07-12 00:00:00.000,3,2,ID1,ABC,3,8,58,1
2016-07-13 00:00:00.000,4,1,ID1,ABC,4,8,59,1
2016-07-13 00:00:00.000,4,2,ID1,ABC,4,8,60,1
2016-07-14 00:00:00.000,5,1,ID1,ABC,5,8,61,1
2016-07-14 00:00:00.000,5,2,ID1,ABC,5,8,62,1
2016-07-15 00:00:00.000,6,1,ID1,ABC,6,8,63,1
2016-07-15 00:00:00.000,6,2,ID1,ABC,6,8,64,1
2016-07-19 00:00:00.000,3,1,ID1,ABC,3,8,65,0
2016-07-19 00:00:00.000,3,2,ID1,ABC,3,8,66,1
2016-07-20 00:00:00.000,4,1,ID1,ABC,4,8,67,1
2016-07-20 00:00:00.000,4,2,ID1,ABC,4,8,68,1
2016-07-21 00:00:00.000,5,1,ID1,ABC,5,8,69,1
2016-07-21 00:00:00.000,5,2,ID1,ABC,5,8,70,1
2016-07-22 00:00:00.000,6,1,ID1,ABC,6,8,71,1
2016-07-22 00:00:00.000,6,2,ID1,ABC,6,8,72,1
2016-07-26 00:00:00.000,3,1,ID1,ABC,3,8,73,0
2016-07-26 00:00:00.000,3,2,ID1,ABC,3,8,74,1
2016-07-27 00:00:00.000,4,1,ID1,ABC,4,8,75,1
2016-07-27 00:00:00.000,4,2,ID1,ABC,4,8,76,1
2016-07-28 00:00:00.000,5,1,ID1,ABC,5,8,77,1
2016-07-28 00:00:00.000,5,2,ID1,ABC,5,8,78,1
2016-07-29 00:00:00.000,6,1,ID1,ABC,6,8,79,1
2016-07-29 00:00:00.000,6,2,ID1,ABC,6,8,80,1
2016-08-02 00:00:00.000,3,1,ID1,ABC,3,8,81,0
2016-08-02 00:00:00.000,3,2,ID1,ABC,3,8,82,1
2016-08-03 00:00:00.000,4,1,ID1,ABC,4,8,83,1
2016-08-03 00:00:00.000,4,2,ID1,ABC,4,8,84,1
2016-08-04 00:00:00.000,5,1,ID1,ABC,5,8,85,1
2016-08-04 00:00:00.000,5,2,ID1,ABC,5,8,86,1
2016-08-05 00:00:00.000,6,1,ID1,ABC,6,8,87,1
2016-08-05 00:00:00.000,6,2,ID1,ABC,6,8,88,1
2016-08-09 00:00:00.000,3,1,ID1,ABC,3,8,89,0
2016-08-09 00:00:00.000,3,2,ID1,ABC,3,8,90,1
2016-08-10 00:00:00.000,4,1,ID1,ABC,4,8,91,1
2016-08-10 00:00:00.000,4,2,ID1,ABC,4,8,92,1
2016-08-11 00:00:00.000,5,1,ID1,ABC,5,8,93,1
2016-08-11 00:00:00.000,5,2,ID1,ABC,5,8,94,1
2016-08-12 00:00:00.000,6,1,ID1,ABC,6,8,95,1
2016-08-12 00:00:00.000,6,2,ID1,ABC,6,8,96,1
2016-08-16 00:00:00.000,3,1,ID1,ABC,3,8,97,0
2016-08-16 00:00:00.000,3,2,ID1,ABC,3,8,98,1
2016-08-17 00:00:00.000,4,1,ID1,ABC,4,8,99,1
2016-08-17 00:00:00.000,4,2,ID1,ABC,4,8,100,1
2016-08-18 00:00:00.000,5,1,ID1,ABC,5,8,101,1
2016-08-18 00:00:00.000,5,2,ID1,ABC,5,8,102,1
2016-08-19 00:00:00.000,6,1,ID1,ABC,6,8,103,1
2016-08-19 00:00:00.000,6,2,ID1,ABC,6,8,104,1
2016-08-23 00:00:00.000,3,1,ID1,ABC,3,8,105,0
2016-08-23 00:00:00.000,3,2,ID1,ABC,3,8,106,1
2016-08-24 00:00:00.000,4,1,ID1,ABC,4,8,107,1
2016-08-24 00:00:00.000,4,2,ID1,ABC,4,8,108,1
2016-08-25 00:00:00.000,5,1,ID1,ABC,5,8,109,1
2016-08-25 00:00:00.000,5,2,ID1,ABC,5,8,110,1
2016-08-26 00:00:00.000,6,1,ID1,ABC,6,8,111,1
2016-08-26 00:00:00.000,6,2,ID1,ABC,6,8,112,1
2016-08-30 00:00:00.000,3,1,ID1,ABC,3,8,113,0
2016-08-30 00:00:00.000,3,2,ID1,ABC,3,8,114,1
2016-08-31 00:00:00.000,4,1,ID1,ABC,4,8,115,1
2016-08-31 00:00:00.000,4,2,ID1,ABC,4,8,116,1
2016-09-01 00:00:00.000,5,1,ID1,ABC,5,8,117,1
2016-09-01 00:00:00.000,5,2,ID1,ABC,5,8,118,1
2016-09-02 00:00:00.000,6,1,ID1,ABC,6,8,119,1
2016-09-02 00:00:00.000,6,2,ID1,ABC,6,8,120,1
2016-09-06 00:00:00.000,3,1,ID1,ABC,3,8,121,0
2016-09-06 00:00:00.000,3,2,ID1,ABC,3,8,122,1
2016-09-07 00:00:00.000,4,1,ID1,ABC,4,8,123,1
2016-09-07 00:00:00.000,4,2,ID1,ABC,4,8,124,1
2016-09-08 00:00:00.000,5,1,ID1,ABC,5,8,125,1
2016-09-08 00:00:00.000,5,2,ID1,ABC,5,8,126,1
2016-09-09 00:00:00.000,6,1,ID1,ABC,6,8,127,1
2016-09-09 00:00:00.000,6,2,ID1,ABC,6,8,128,1
2016-09-13 00:00:00.000,3,1,ID1,ABC,3,8,129,0
2016-09-13 00:00:00.000,3,2,ID1,ABC,3,8,130,1
2016-09-14 00:00:00.000,4,1,ID1,ABC,4,8,131,1
2016-09-14 00:00:00.000,4,2,ID1,ABC,4,8,132,1
2016-09-15 00:00:00.000,5,1,ID1,ABC,5,8,133,1
2016-09-15 00:00:00.000,5,2,ID1,ABC,5,8,134,1
2016-09-16 00:00:00.000,6,1,ID1,ABC,6,8,135,1
2016-09-16 00:00:00.000,6,2,ID1,ABC,6,8,136,1
2016-09-20 00:00:00.000,3,1,ID1,ABC,3,8,137,0
2016-09-20 00:00:00.000,3,2,ID1,ABC,3,8,138,1
2016-09-21 00:00:00.000,4,1,ID1,ABC,4,8,139,1
2016-09-21 00:00:00.000,4,2,ID1,ABC,4,8,140,1
2016-09-22 00:00:00.000,5,1,ID1,ABC,5,8,141,1
2016-09-22 00:00:00.000,5,2,ID1,ABC,5,8,142,1
2016-09-23 00:00:00.000,6,1,ID1,ABC,6,8,143,1
2016-09-23 00:00:00.000,6,2,ID1,ABC,6,8,144,1
2016-09-27 00:00:00.000,3,1,ID1,ABC,3,8,145,0
2016-09-27 00:00:00.000,3,2,ID1,ABC,3,8,146,1
2016-09-28 00:00:00.000,4,1,ID1,ABC,4,8,147,1
2016-09-28 00:00:00.000,4,2,ID1,ABC,4,8,148,1
2016-09-29 00:00:00.000,5,1,ID1,ABC,5,8,149,1
2016-09-29 00:00:00.000,5,2,ID1,ABC,5,8,150,1
2016-09-30 00:00:00.000,6,1,ID1,ABC,6,8,151,1
2016-09-30 00:00:00.000,6,2,ID1,ABC,6,8,152,1
2016-10-04 00:00:00.000,3,1,ID1,ABC,3,8,153,0
2016-10-04 00:00:00.000,3,2,ID1,ABC,3,8,154,1
2016-10-05 00:00:00.000,4,1,ID1,ABC,4,8,155,1
2016-10-05 00:00:00.000,4,2,ID1,ABC,4,8,156,1
2016-10-06 00:00:00.000,5,1,ID1,ABC,5,8,157,1
2016-10-06 00:00:00.000,5,2,ID1,ABC,5,8,158,1
2016-10-07 00:00:00.000,6,1,ID1,ABC,6,8,159,1
2016-10-07 00:00:00.000,6,2,ID1,ABC,6,8,160,1
2016-10-11 00:00:00.000,3,1,ID1,ABC,3,8,161,0
2016-10-11 00:00:00.000,3,2,ID1,ABC,3,8,162,1
2016-10-12 00:00:00.000,4,1,ID1,ABC,4,8,163,1
2016-10-12 00:00:00.000,4,2,ID1,ABC,4,8,164,1
2016-10-13 00:00:00.000,5,1,ID1,ABC,5,8,165,1
2016-10-13 00:00:00.000,5,2,ID1,ABC,5,8,166,1
2016-10-14 00:00:00.000,6,1,ID1,ABC,6,8,167,1
2016-10-14 00:00:00.000,6,2,ID1,ABC,6,8,168,1
2016-10-18 00:00:00.000,3,1,ID1,ABC,3,8,169,0
2016-10-18 00:00:00.000,3,2,ID1,ABC,3,8,170,1
2016-10-19 00:00:00.000,4,1,ID1,ABC,4,8,171,1
2016-10-19 00:00:00.000,4,2,ID1,ABC,4,8,172,1
2016-10-20 00:00:00.000,5,1,ID1,ABC,5,8,173,1
2016-10-20 00:00:00.000,5,2,ID1,ABC,5,8,174,1
2016-10-21 00:00:00.000,6,1,ID1,ABC,6,8,175,1
2016-10-21 00:00:00.000,6,2,ID1,ABC,6,8,176,1
2016-10-25 00:00:00.000,3,1,ID1,ABC,3,8,177,0
2016-10-25 00:00:00.000,3,2,ID1,ABC,3,8,178,1
2016-10-26 00:00:00.000,4,1,ID1,ABC,4,8,179,1
2016-10-26 00:00:00.000,4,2,ID1,ABC,4,8,180,1
2016-10-27 00:00:00.000,5,1,ID1,ABC,5,8,181,1
2016-10-27 00:00:00.000,5,2,ID1,ABC,5,8,182,1
2016-10-28 00:00:00.000,6,1,ID1,ABC,6,8,183,1
2016-10-28 00:00:00.000,6,2,ID1,ABC,6,8,184,1
2016-11-01 00:00:00.000,3,1,ID1,ABC,3,8,185,0
2016-11-01 00:00:00.000,3,2,ID1,ABC,3,8,186,1
2016-11-02 00:00:00.000,4,1,ID1,ABC,4,8,187,1
2016-11-02 00:00:00.000,4,2,ID1,ABC,4,8,188,1
2016-11-03 00:00:00.000,5,1,ID1,ABC,5,8,189,1
2016-11-03 00:00:00.000,5,2,ID1,ABC,5,8,190,1
2016-11-04 00:00:00.000,6,1,ID1,ABC,6,8,191,1
2016-11-04 00:00:00.000,6,2,ID1,ABC,6,8,192,1
2016-11-08 00:00:00.000,3,1,ID1,ABC,3,8,193,0
2016-11-08 00:00:00.000,3,2,ID1,ABC,3,8,194,1
2016-11-09 00:00:00.000,4,1,ID1,ABC,4,8,195,1
2016-11-09 00:00:00.000,4,2,ID1,ABC,4,8,196,1
2016-11-10 00:00:00.000,5,1,ID1,ABC,5,8,197,1
2016-11-10 00:00:00.000,5,2,ID1,ABC,5,8,198,1
2016-11-11 00:00:00.000,6,1,ID1,ABC,6,8,199,1
2016-11-11 00:00:00.000,6,2,ID1,ABC,6,8,200,1
In SQL Server you can bypass 100 iterations default limitation by specifying OPTION (MAXRECURSION X) with your query. Where X is maximum number of iterations, and it can have values from 0 (no limitation) up to 32767 (maximum defined limitation).
If you are uncertain how many iterations you might expect or amount of expected iterations exceeds 32767 then you might consider using MAXRECURSION 0 option.
with r (PresentationDate, Presentationid,Name,MaxSession,PresentationDay,rn,blocknum,pos,lastmaxrow) AS
(
select PresentationDate, Presentationid, Name, MaxSession, PresentationDay, rn,1,1,MaxSession
from presentation
where rn=1
union all
select u.PresentationDate, u.Presentationid, u.Name, u.MaxSession, u.PresentationDay, u.rn,
case
when r.pos=r.lastmaxrow or u.newblock=0 then
r.blocknum+1
else r.blocknum
end,
case
when r.pos = r.lastmaxrow or u.newblock=0 then 1
else r.pos+1
end,
case
when r.pos = r.lastmaxrow or u.newblock=0 then r.lastmaxrow
else r.MaxSession
end
from presentation u
join r
on u.Presentationid=r.Presentationid and u.rn=r.rn+1
)
select min(r.PresentationDate) as fromDate, max(r.PresentationDate) as todate,
r.Presentationid, r.Name
from r
group by r.Presentationid, r.Name, r.blocknum
order by r.Name, r.blocknum
OPTION (MAXRECURSION 0)
You can read about MAXRECURSION in context of CTE here
Gaps and Islands
Here is the minimum query that demonstrate the concept.
There are plenty of tutorials that explain this type of problem in detail.
select PresentationID
,min(PresentationDate) as from_date
,max(PresentationDate) as to_date
from (select PresentationID
,PresentationDate
,dense_rank () over
(
partition by PresentationID
order by PresentationDate
) as dr
from presentation
) p
group by PresentationID
,dateadd(day,-dr,PresentationDate)
order by PresentationID
,to_date
+----------------+------------+------------+
| PresentationID | from_date | to_date |
+----------------+------------+------------+
| ID1 | 2016-05-24 | 2016-05-27 |
| ID1 | 2016-05-31 | 2016-06-03 |
| ID1 | 2016-06-07 | 2016-06-10 |
| ID1 | 2016-06-14 | 2016-06-17 |
| ID1 | 2016-06-21 | 2016-06-24 |
| ID1 | 2016-06-28 | 2016-07-01 |
| ID1 | 2016-07-05 | 2016-07-08 |
| ID1 | 2016-07-12 | 2016-07-15 |
| ID1 | 2016-07-19 | 2016-07-22 |
| ID1 | 2016-07-26 | 2016-07-29 |
| ID1 | 2016-08-02 | 2016-08-05 |
| ID1 | 2016-08-09 | 2016-08-12 |
| ID1 | 2016-08-16 | 2016-08-19 |
| ID1 | 2016-08-23 | 2016-08-26 |
...
Here are some data That will help you to understand what's going on here.
+----------------+------------------+----+------------+
| PresentationID | PresentationDate | dr | dateadd |
+----------------+------------------+----+------------+
| ID1 | 2016-05-24 | 1 | 2016-05-23 |
| ID1 | 2016-05-24 | 1 | 2016-05-23 |
| ID1 | 2016-05-25 | 2 | 2016-05-23 |
| ID1 | 2016-05-25 | 2 | 2016-05-23 |
| ID1 | 2016-05-26 | 3 | 2016-05-23 |
| ID1 | 2016-05-26 | 3 | 2016-05-23 |
| ID1 | 2016-05-27 | 4 | 2016-05-23 |
| ID1 | 2016-05-27 | 4 | 2016-05-23 |
| ID1 | 2016-05-31 | 5 | 2016-05-26 |
| ID1 | 2016-05-31 | 5 | 2016-05-26 |
| ID1 | 2016-06-01 | 6 | 2016-05-26 |
| ID1 | 2016-06-01 | 6 | 2016-05-26 |
| ID1 | 2016-06-02 | 7 | 2016-05-26 |
| ID1 | 2016-06-02 | 7 | 2016-05-26 |
| ID1 | 2016-06-03 | 8 | 2016-05-26 |
| ID1 | 2016-06-03 | 8 | 2016-05-26 |
| ID1 | 2016-06-07 | 9 | 2016-05-29 |
| ID1 | 2016-06-07 | 9 | 2016-05-29 |
| ID1 | 2016-06-08 | 10 | 2016-05-29 |
| ID1 | 2016-06-08 | 10 | 2016-05-29 |
...
Related
SQL - Split open & Close time Into intervals of 30 minutes
Purpose: I work in Hospitality Industry. I want to understand at what time the Restaurant is full and what time it is less busy. I have the opening and closing times, I want to split it 30 minute interval period. I would really appreciate if you could ease help me. Thanking you in advance Table Check# Open CloseTime 25484 17:34 18:06 25488 18:04 21:22 Output Check# Open Close Duration 25484 17:34 18:00 0:25 25484 18:00 18:30 0:30 25488 18:08 18:30 0:21 25488 18:30 19:00 0:30 25488 19:00 19:30 0:30 25488 19:30 20:00 0:30 25488 20:00 20:30 0:30 25488 20:30 21:00 0:30 25488 21:00 21:30 0:30 I am new to SQL. I am good at Excel, but due to its limitations i want to use SQL. I just know the basics in SQL. I have tried on the google, but could not find solution to it. All i can see use of Date Keywords, but not the Field name in the code, hence i am unable to use them.
Could you try this, it works in MySQL 8.0: WITH RECURSIVE times AS ( SELECT time '0:00' AS `Open`, time '0:30' as `Close` UNION ALL SELECT addtime(`Open`, '0:30'), addtime(`Close`, '0:30') FROM times WHERE `Open` < time '23:30' ) SELECT c.`Check`, greatest(t.`Open`, c.`Open`) `Open`, least(t.`Close`, c.`CloseTime`) `Close`, timediff(least(t.`Close`, c.`CloseTime`), greatest(t.`Open`, c.`Open`)) `Duration` FROM times t JOIN checks c ON (c.`Open` < t.`Close` AND c.`CloseTime` > t.`Open`); | Check | Open | Close | Duration | | ----- | -------- | -------- | -------- | | 25484 | 17:34:00 | 18:00:00 | 00:26:00 | | 25484 | 18:00:00 | 18:06:00 | 00:06:00 | | 25488 | 18:04:00 | 18:30:00 | 00:26:00 | | 25488 | 18:30:00 | 19:00:00 | 00:30:00 | | 25488 | 19:00:00 | 19:30:00 | 00:30:00 | | 25488 | 19:30:00 | 20:00:00 | 00:30:00 | | 25488 | 20:00:00 | 20:30:00 | 00:30:00 | | 25488 | 20:30:00 | 21:00:00 | 00:30:00 | | 25488 | 21:00:00 | 21:22:00 | 00:22:00 | ->Fiddle
This works for SQL Server 2019: WITH times([Open], [Close]) AS ( SELECT cast({t'00:00:00'} as time) as "Open", cast({t'00:30:00'} as time) as "Close" UNION ALL SELECT dateadd(minute, 30, [Open]), dateadd(minute, 30, [Close]) FROM times WHERE [Open] < cast({t'23:30:00'} as time) ) SELECT c.[Check], iif(t.[Open] > c.[Open], t.[Open], c.[Open]) as [Open], iif(t.[Close] < c.[CloseTime], t.[Close], c.[CloseTime]) as [Close], datediff(minute, iif(t.[Open] > c.[Open], t.[Open], c.[Open]), iif(t.[Close] < c.[CloseTime], t.[Close], c.[CloseTime])) Duration FROM times t JOIN checks c ON (c.[Open] < t.[Close] AND c.[CloseTime] > t.[Open]); Check | Open | Close | Duration 25484 | 17:34:00.0000000 | 18:00:00.0000000 | 26 25484 | 18:00:00.0000000 | 18:06:00.0000000 | 6 25488 | 18:04:00.0000000 | 18:30:00.0000000 | 26 25488 | 18:30:00.0000000 | 19:00:00.0000000 | 30 25488 | 19:00:00.0000000 | 19:30:00.0000000 | 30 25488 | 19:30:00.0000000 | 20:00:00.0000000 | 30 25488 | 20:00:00.0000000 | 20:30:00.0000000 | 30 25488 | 20:30:00.0000000 | 21:00:00.0000000 | 30 25488 | 21:00:00.0000000 | 21:22:00.0000000 | 22 ->Fiddle
SQL query for setting column based on last seven entries
Problem I am having trouble figuring out how to create a query that can tell if any userentry is preceded by 7 days without any activity (secondsPlayed == 0) and if so, then indicate it with the value of 1, otherwise 0. This also means that if the user has less than 7 entries, the value will be 0 across all entries. Input table: +------------------------------+-------------------------+---------------+ | userid | estimationDate | secondsPlayed | +------------------------------+-------------------------+---------------+ | a | 2016-07-14 00:00:00 UTC | 192.5 | | a | 2016-07-15 00:00:00 UTC | 357.3 | | a | 2016-07-16 00:00:00 UTC | 0 | | a | 2016-07-17 00:00:00 UTC | 0 | | a | 2016-07-18 00:00:00 UTC | 0 | | a | 2016-07-19 00:00:00 UTC | 0 | | a | 2016-07-20 00:00:00 UTC | 0 | | a | 2016-07-21 00:00:00 UTC | 0 | | a | 2016-07-22 00:00:00 UTC | 0 | | a | 2016-07-23 00:00:00 UTC | 0 | | a | 2016-07-24 00:00:00 UTC | 0 | | ---------------------------- | ---------------------- | ---- | | b | 2016-07-02 00:00:00 UTC | 31.2 | | b | 2016-07-03 00:00:00 UTC | 42.1 | | b | 2016-07-04 00:00:00 UTC | 41.9 | | b | 2016-07-05 00:00:00 UTC | 43.2 | | b | 2016-07-06 00:00:00 UTC | 91.5 | | b | 2016-07-07 00:00:00 UTC | 0 | | b | 2016-07-08 00:00:00 UTC | 0 | | b | 2016-07-09 00:00:00 UTC | 239.1 | | b | 2016-07-10 00:00:00 UTC | 0 | +------------------------------+-------------------------+---------------+ The intended output table should look like this: Output table: +------------------------------+-------------------------+---------------+----------+ | userid | estimationDate | secondsPlayed | inactive | +------------------------------+-------------------------+---------------+----------+ | a | 2016-07-14 00:00:00 UTC | 192.5 | 0 | | a | 2016-07-15 00:00:00 UTC | 357.3 | 0 | | a | 2016-07-16 00:00:00 UTC | 0 | 0 | | a | 2016-07-17 00:00:00 UTC | 0 | 0 | | a | 2016-07-18 00:00:00 UTC | 0 | 0 | | a | 2016-07-19 00:00:00 UTC | 0 | 0 | | a | 2016-07-20 00:00:00 UTC | 0 | 0 | | a | 2016-07-21 00:00:00 UTC | 0 | 0 | | a | 2016-07-22 00:00:00 UTC | 0 | 1 | | a | 2016-07-23 00:00:00 UTC | 0 | 1 | | a | 2016-07-24 00:00:00 UTC | 0 | 1 | | ---------------------------- | ----------------------- | ----- | ----- | | b | 2016-07-02 00:00:00 UTC | 31.2 | 0 | | b | 2016-07-03 00:00:00 UTC | 42.1 | 0 | | b | 2016-07-04 00:00:00 UTC | 41.9 | 0 | | b | 2016-07-05 00:00:00 UTC | 43.2 | 0 | | b | 2016-07-06 00:00:00 UTC | 91.5 | 0 | | b | 2016-07-07 00:00:00 UTC | 0 | 0 | | b | 2016-07-08 00:00:00 UTC | 0 | 0 | | b | 2016-07-09 00:00:00 UTC | 239.1 | 0 | | b | 2016-07-10 00:00:00 UTC | 0 | 0 | +------------------------------+-------------------------+---------------+----------+ Thoughts At first I was thinking about using the Lag function with a 7 offset, but this would obviously not relate to any of the subjects in between. I was also thinking about creating a rolling window/average for a period of 7 days and evaluating if this is above 0. However this might be a bit above my skill level. Any chance anyone has a good solution to this problem.
Assuming that you have data every day (which seems like a reasonable assumption), you can sum a window function: select t.*, (case when sum(secondsplayed) over (partition by userid order by estimationdate rows between 6 preceding and current row ) = 0 and row_number() over (partition by userid order by estimationdate) >= 7 then 1 else 0 end) as inactive from t; In addition to no holes in the dates, this also assumes that secondsplayed is never negative. (Negative values can easily be incorporated into the logic, but that seems unnecessary.)
In my experience this type of input tables do not consist of inactivity entries and usually look like this (only activity entries are present here) Input table: +------------------------------+-------------------------+---------------+ | userid | estimationDate | secondsPlayed | +------------------------------+-------------------------+---------------+ | a | 2016-07-14 00:00:00 UTC | 192.5 | | a | 2016-07-15 00:00:00 UTC | 357.3 | | ---------------------------- | ---------------------- | ---- | | b | 2016-07-02 00:00:00 UTC | 31.2 | | b | 2016-07-03 00:00:00 UTC | 42.1 | | b | 2016-07-04 00:00:00 UTC | 41.9 | | b | 2016-07-05 00:00:00 UTC | 43.2 | | b | 2016-07-06 00:00:00 UTC | 91.5 | | b | 2016-07-09 00:00:00 UTC | 239.1 | +------------------------------+-------------------------+---------------+ So, below is for BigQuery Standard SQL and input as above #standardSQL WITH `project.dataset.table` AS ( SELECT 'a' userid, TIMESTAMP '2016-07-14 00:00:00 UTC' estimationDate, 192.5 secondsPlayed UNION ALL SELECT 'a', '2016-07-15 00:00:00 UTC', 357.3 UNION ALL SELECT 'b', '2016-07-02 00:00:00 UTC', 31.2 UNION ALL SELECT 'b', '2016-07-03 00:00:00 UTC', 42.1 UNION ALL SELECT 'b', '2016-07-04 00:00:00 UTC', 41.9 UNION ALL SELECT 'b', '2016-07-05 00:00:00 UTC', 43.2 UNION ALL SELECT 'b', '2016-07-06 00:00:00 UTC', 91.5 UNION ALL SELECT 'b', '2016-07-09 00:00:00 UTC', 239.1 ), time_frame AS ( SELECT day FROM UNNEST(GENERATE_DATE_ARRAY('2016-07-02', '2016-07-24')) day ) SELECT users.userid, day, IFNULL(secondsPlayed, 0) secondsPlayed, CAST(1 - SIGN(SUM(IFNULL(secondsPlayed, 0)) OVER( PARTITION BY users.userid ORDER BY UNIX_DATE(day) RANGE BETWEEN 6 PRECEDING AND CURRENT ROW )) AS INT64) AS inactive FROM time_frame tf CROSS JOIN (SELECT DISTINCT userid FROM `project.dataset.table`) users LEFT JOIN `project.dataset.table` t ON day = DATE(estimationDate) AND users.userid = t.userid ORDER BY userid, day with result Row userid day secondsPlayed inactive ... 13 a 2016-07-14 192.5 0 14 a 2016-07-15 357.3 0 15 a 2016-07-15 357.3 0 16 a 2016-07-16 0.0 0 17 a 2016-07-17 0.0 0 18 a 2016-07-18 0.0 0 19 a 2016-07-19 0.0 0 20 a 2016-07-20 0.0 0 21 a 2016-07-21 0.0 0 22 a 2016-07-22 0.0 1 23 a 2016-07-23 0.0 1 24 a 2016-07-24 0.0 1 25 b 2016-07-02 31.2 0 26 b 2016-07-03 42.1 0 27 b 2016-07-04 41.9 0 28 b 2016-07-05 43.2 0 29 b 2016-07-06 91.5 0 30 b 2016-07-07 0.0 0 31 b 2016-07-08 0.0 0 32 b 2016-07-09 239.1 0 33 b 2016-07-10 0.0 0 ...
Postgres, Update TIMESTAMP to current date but preserve time of day
In my Postgres database, I have the following table: SELECT start_at, end_at FROM schedules; +---------------------+---------------------+ | start_at | end_at | |---------------------+---------------------| | 2016-09-05 16:30:00 | 2016-09-05 17:30:00 | | 2016-09-05 17:30:00 | 2016-09-05 18:30:00 | | 2017-08-13 03:00:00 | 2017-08-13 07:00:00 | | 2017-08-13 03:00:00 | 2017-08-13 07:00:00 | | 2017-08-13 18:42:26 | 2017-08-13 21:30:46 | | 2017-08-10 00:00:00 | 2017-08-10 03:30:00 | | 2017-08-09 18:00:00 | 2017-08-10 03:00:00 | | 2017-08-06 23:00:00 | 2017-08-07 03:00:00 | | 2017-08-07 01:00:00 | 2017-08-07 03:48:20 | | 2017-08-07 01:00:00 | 2017-08-07 03:48:20 | | 2017-08-07 18:05:00 | 2017-08-07 20:53:20 | | 2017-08-07 14:00:00 | 2017-08-08 01:00:00 | | 2017-08-07 18:00:00 | 2017-08-07 20:48:20 | | 2017-08-08 08:00:00 | 2017-08-09 00:00:00 | | 2017-08-09 21:30:00 | 2017-08-10 00:18:20 | | 2017-08-13 03:53:26 | 2017-08-13 06:41:46 | +---------------------+---------------------+ Assume I also have an ID column, what I want to do is update all the start and end times to be for today (now), what is the most efficient SQL to accomplish this? My table could have millions of rows.
the best I can think of is this: update schedules set start_at = current_date + start_at::time , end_at = current_date + end_at::time WHERE start_at::date <> current_date or end_at::date <> current_date; The arithmetic is fast compared to accessing the rows. if not all rows need updating, the where clause will help efficiency. Updates are expensive.
SQL group aggregate by date range columns in another table
I need a query to group an aggregate in one table by date ranges in another table. Table 1 weeknumber | weekyear | weekstart | weekend ------------+----------+------------+------------ 18 | 2016 | 2016-02-01 | 2016-02-08 19 | 2016 | 2016-02-08 | 2016-02-15 20 | 2016 | 2016-02-15 | 2016-02-22 21 | 2016 | 2016-02-22 | 2016-02-29 22 | 2016 | 2016-02-29 | 2016-03-07 23 | 2016 | 2016-03-07 | 2016-03-14 24 | 2016 | 2016-03-14 | 2016-03-21 25 | 2016 | 2016-03-21 | 2016-03-28 26 | 2016 | 2016-03-28 | 2016-04-04 27 | 2016 | 2016-04-04 | 2016-04-11 28 | 2016 | 2016-04-11 | 2016-04-18 29 | 2016 | 2016-04-18 | 2016-04-25 30 | 2016 | 2016-04-25 | 2016-05-02 31 | 2016 | 2016-05-02 | 2016-05-09 32 | 2016 | 2016-05-09 | 2016-05-16 33 | 2016 | 2016-05-16 | 2016-05-23 34 | 2016 | 2016-05-23 | 2016-05-30 35 | 2016 | 2016-05-30 | 2016-06-06 36 | 2016 | 2016-06-06 | 2016-06-13 37 | 2016 | 2016-06-13 | 2016-06-20 38 | 2016 | 2016-06-20 | 2016-06-27 39 | 2016 | 2016-06-27 | 2016-07-04 40 | 2016 | 2016-07-04 | 2016-07-11 41 | 2016 | 2016-07-11 | 2016-07-18 42 | 2016 | 2016-07-18 | 2016-07-25 43 | 2016 | 2016-07-25 | 2016-08-01 44 | 2016 | 2016-08-01 | 2016-08-08 45 | 2016 | 2016-08-08 | 2016-08-15 46 | 2016 | 2016-08-15 | 2016-08-22 47 | 2016 | 2016-08-22 | 2016-08-29 48 | 2016 | 2016-08-29 | 2016-09-05 49 | 2016 | 2016-09-05 | 2016-09-12 Table 2 accountid | rdate | fee1 | fee2 | fee3 | fee4 -----------+------------+------+------+------+------ 481164 | 2015-12-22 | 8 | 1 | 5 | 1 481164 | 2002-12-22 | 1 | 0 | 0 | 0 481166 | 2015-12-22 | 1 | 0 | 0 | 0 481166 | 2016-10-20 | 14 | 0 | 0 | 0 481166 | 2016-10-02 | 5 | 0 | 0 | 0 481166 | 2016-01-06 | 18 | 4 | 0 | 5 482136 | 2016-07-04 | 18 | 0 | 0 | 0 481164 | 2016-07-04 | 2 | 3 | 4 | 5 481164 | 2016-06-28 | 34 | 0 | 0 | 0 481166 | 2016-07-20 | 50 | 0 | 0 | 69 481166 | 2016-07-13 | 16 | 0 | 0 | 5 481166 | 2016-09-15 | 8 | 0 | 0 | 2 481166 | 2016-10-03 | 8 | 0 | 0 | 0 I need to aggregate fee1+fee2+fee3+fee4 for rdates in each date range(weekstart,weekend) in table 1 and then group by accountid. Something like this: accountid | weekstart | weekend | SUM -----------+------------+------------+------ 481164 | 2016-02-01 | 2016-02-08 | 69 481166 | 2016-02-01 | 2016-02-08 | 44 481164 | 2016-02-08 | 2016-02-15 | 22 481166 | 2016-02-08 | 2016-02-15 | 12
select accountid, weekstart, weekend, sum(fee1 + fee2 + fee3 + fee4) as total_fee from table2 inner join table1 on table2.rdate >= table1.weekstart and table2.rdate < table1.weekend group by accountid, weekstart, weekend; Just a thing: weeknumber | weekyear | weekstart | weekend ------------+----------+------------+------------ 18 | 2016 | 2016-02-01 | 2016-02-08 19 | 2016 | 2016-02-08 | 2016-02-15 weekend for week 18 should be 2016-02-07, because 2016-02-08 is weekstart for week 19. weeknumber | weekyear | weekstart | weekend ------------+----------+------------+------------ 18 | 2016 | 2016-02-01 | 2016-02-07 19 | 2016 | 2016-02-08 | 2016-02-14 Check it here: http://rextester.com/NCBO56250
SQL consolidate consecutive date
Hi could anyone help me with the SQL Query. I want to display all the consecutive dates in the db where there is at least 2 consecutive dates. Below are an example of the output what i was hoping for. here is a list of dates: 2016-06-24 00:00:00.000 2016-06-24 00:00:00.000 2016-06-24 00:00:00.000 2016-06-25 00:00:00.000 2016-06-25 00:00:00.000 2016-06-26 00:00:00.000 2016-05-26 00:00:00.000 2016-05-25 00:00:00.000 2016-04-04 00:00:00.000 2016-06-26 00:00:00.000 ----------output---------- | Start Date | End Date | Count | consecutive Date | | 2016-05-25 00:00:00.000 | 2016-05-25 00:00:00.000 | 1 | 2 | | 2016-05-25 00:00:00.000 | 2016-05-26 00:00:00.000 | 1 | 2 | | 2016-06-24 00:00:00.000 | 2016-06-25 00:00:00.000 | 2 | 2 | | 2016-06-24 00:00:00.000 | 2016-06-26 00:00:00.000 | 2 | 3 | | 2016-06-25 00:00:00.000 | 2016-06-26 00:00:00.000 | 2 | 2 | This is what I have currently: WITH t AS ( SELECT SWITCHOFFSET(CONVERT(DATETIMEOFFSET, dateAvailable), '+08:00') d, ROW_NUMBER() OVER(ORDER BY dateAvailable) i FROM user_dateTbl GROUP BY dateAvailable ) SELECT MIN(d),MAX(d) FROM t GROUP BY DATEDIFF(day,i,d) please help me out thank you. If your unsure of what I am writing do feel free to write in the comment below.