Postgres, Update TIMESTAMP to current date but preserve time of day - sql
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.
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
PostgreSQL count max number of concurrent user sessions per hour
Situation We have a PostgreSQL 9.1 database containing user sessions with login date/time and logout date/time per row. Table looks like this: user_id | login_ts | logout_ts ------------+--------------+-------------------------------- USER1 | 2021-02-03 09:23:00 | 2021-02-03 11:44:00 USER2 | 2021-02-03 10:49:00 | 2021-02-03 13:30:00 USER3 | 2021-02-03 13:32:00 | 2021-02-03 15:31:00 USER4 | 2021-02-04 13:50:00 | 2021-02-04 14:53:00 USER5 | 2021-02-04 14:44:00 | 2021-02-04 15:21:00 USER6 | 2021-02-04 14:52:00 | 2021-02-04 17:59:00 Goal Would like to get the max number of concurrent users for each 24 hours of each day in the time range. Like this: date | hour | sessions -----------+-------+----------- 2021-02-03 | 01:00 | 0 2021-02-03 | 02:00 | 0 2021-02-03 | 03:00 | 0 2021-02-03 | 04:00 | 0 2021-02-03 | 05:00 | 0 2021-02-03 | 06:00 | 0 2021-02-03 | 07:00 | 0 2021-02-03 | 08:00 | 0 2021-02-03 | 09:00 | 1 2021-02-03 | 10:00 | 2 2021-02-03 | 11:00 | 2 2021-02-03 | 12:00 | 1 2021-02-03 | 13:00 | 1 2021-02-03 | 14:00 | 1 2021-02-03 | 15:00 | 0 2021-02-03 | 16:00 | 0 2021-02-03 | 17:00 | 0 2021-02-03 | 18:00 | 0 2021-02-03 | 19:00 | 0 2021-02-03 | 20:00 | 0 2021-02-03 | 21:00 | 0 2021-02-03 | 22:00 | 0 2021-02-03 | 23:00 | 0 2021-02-03 | 24:00 | 0 2021-02-04 | 01:00 | 0 2021-02-04 | 02:00 | 0 2021-02-04 | 03:00 | 0 2021-02-04 | 04:00 | 0 2021-02-04 | 05:00 | 0 2021-02-04 | 06:00 | 0 2021-02-04 | 07:00 | 0 2021-02-04 | 08:00 | 0 2021-02-04 | 09:00 | 0 2021-02-04 | 10:00 | 0 2021-02-04 | 11:00 | 0 2021-02-04 | 12:00 | 0 2021-02-04 | 13:00 | 1 2021-02-04 | 14:00 | 3 2021-02-04 | 15:00 | 1 2021-02-04 | 16:00 | 1 2021-02-04 | 17:00 | 1 2021-02-04 | 18:00 | 0 2021-02-04 | 19:00 | 0 2021-02-04 | 20:00 | 0 2021-02-04 | 21:00 | 0 2021-02-04 | 22:00 | 0 2021-02-04 | 23:00 | 0 2021-02-04 | 24:00 | 0 Considerations "Concurrent" means at the same point in time. Thus user2 and user3 do not overlap for 13:00, but user4 and user6 do overlap for 14:00 even though they only overlap for 1 minute. User sessions can span multiple hours and would thus count for each hour they are part of. Each user can only be online once at one point in time. If there are no users for a particular hour, this should return 0. Similar questions A similar question was answered here: Count max. number of concurrent user sessions per day by Erwin Brandstetter. However, this is per day rather than per hour, and I am apparently too much of a noob at postgreSQL to be able to translate it into hourly so I'm hoping someone can help.
I would decompose this into two problems: Find the number of overlaps and when they begin and end. Find the hours. Note two things: I am assuming that '2014-04-03 17:59:00' is a typo. The following goes by the beginning of the hour and puts the date/hour in a single column. First, calculate the overlaps. For this, unpivot the logins and logout. Put in a counter of +1 for logins and -1 for logouts and do a cumulative sum. This looks like: with overlap as ( select v.ts, sum(v.inc) as inc, sum(sum(v.inc)) over (order by v.ts) as num_overlaps, lead(v.ts) over (order by v.ts) as next_ts from sessions s cross join lateral (values (login_ts, 1), (logout_ts, -1)) v(ts, inc) group by v.ts ) select * from overlap order by ts; For the next step, use generate_series() to generate timestamps one hour apart. Look for the maximum value during that period using left join and group by: with overlap as ( select v.ts, sum(v.inc) as inc, sum(sum(v.inc)) over (order by v.ts) as num_overlaps, lead(v.ts) over (order by v.ts) as next_ts from sessions s cross join lateral (values (login_ts, 1), (logout_ts, -1)) v(ts, inc) group by v.ts ) select gs.hh, coalesce(max(o.num_overlaps), 0) as num_overlaps from generate_series('2021-02-03'::date, '2021-02-05'::date, interval '1 hour') gs(hh) left join overlap o on o.ts < gs.hh + interval '1 hour' and o.next_ts > gs.hh group by gs.hh order by gs.hh; Here is a db<>fiddle using your data fixed with the a reasonable logout time for the last record.
For any time period you can calculate number of concurrent sesions using OVERLAPS operator in SQL: CREATE TEMP TABLE sessions ( user_id text not null, login_ts timestamp, logout_ts timestamp ); INSERT INTO sessions SELECT 'webuser', d, d+((1+random()*300)::text||' seconds')::interval FROM generate_series( '2021-02-28 07:42'::timestamp, '2021-03-01 07:42'::timestamp, '5 seconds'::interval) AS d; SELECT s1.user_id, s1.login_ts, s1.logout_ts, (select count(*) FROM sessions s2 WHERE (s2.login_ts, s2.logout_ts) OVERLAPS (s1.login_ts, s1.logout_ts)) AS parallel_sessions FROM sessions s1 LIMIT 10; user_id | login_ts | logout_ts | parallel_sessions ---------+---------------------+----------------------------+------------------ webuser | 2021-02-28 07:42:00 | 2021-02-28 07:42:25.528594 | 6 webuser | 2021-02-28 07:42:05 | 2021-02-28 07:45:50.513769 | 47 webuser | 2021-02-28 07:42:10 | 2021-02-28 07:44:18.810066 | 28 webuser | 2021-02-28 07:42:15 | 2021-02-28 07:45:17.3888 | 40 webuser | 2021-02-28 07:42:20 | 2021-02-28 07:43:14.325476 | 15 webuser | 2021-02-28 07:42:25 | 2021-02-28 07:43:44.174841 | 21 webuser | 2021-02-28 07:42:30 | 2021-02-28 07:43:32.679052 | 18 webuser | 2021-02-28 07:42:35 | 2021-02-28 07:45:12.554117 | 38 webuser | 2021-02-28 07:42:40 | 2021-02-28 07:46:37.94311 | 55 webuser | 2021-02-28 07:42:45 | 2021-02-28 07:43:08.398444 | 13 (10 rows) This work well on small data sets but for better performance, use PostgreSQL Range Types as below. This works on postgres 9.2 and later. ALTER TABLE sessions ADD timerange tsrange; UPDATE sessions SET timerange = tsrange(login_ts,logout_ts); CREATE INDEX ON sessions USING gist (timerange); CREATE TEMP TABLE level1 AS SELECT s1.user_id, s1.login_ts, s1.logout_ts, (select count(*) FROM sessions s2 WHERE s2.timerange && s1.timerange) AS parallel_sessions FROM sessions s1; SELECT date_trunc('hour',login_ts) AS hour, count(*), max(parallel_sessions) FROM level1 GROUP BY hour; hour | count | max ---------------------+-------+----- 2021-02-28 14:00:00 | 720 | 98 2021-03-01 03:00:00 | 720 | 99 2021-03-01 06:00:00 | 720 | 94 2021-02-28 09:00:00 | 720 | 96 2021-02-28 10:00:00 | 720 | 97 2021-02-28 18:00:00 | 720 | 94 2021-02-28 11:00:00 | 720 | 97 2021-03-01 00:00:00 | 720 | 97 2021-02-28 19:00:00 | 720 | 99 2021-02-28 16:00:00 | 720 | 94 2021-02-28 17:00:00 | 720 | 95 2021-03-01 02:00:00 | 720 | 99 2021-02-28 08:00:00 | 720 | 96 2021-02-28 23:00:00 | 720 | 94 2021-03-01 07:00:00 | 505 | 92 2021-03-01 04:00:00 | 720 | 95 2021-02-28 21:00:00 | 720 | 97 2021-03-01 01:00:00 | 720 | 93 2021-02-28 22:00:00 | 720 | 96 2021-03-01 05:00:00 | 720 | 93 2021-02-28 20:00:00 | 720 | 95 2021-02-28 13:00:00 | 720 | 95 2021-02-28 12:00:00 | 720 | 97 2021-02-28 15:00:00 | 720 | 98 2021-02-28 07:00:00 | 216 | 93 (25 rows)
hive server pushing parquet timestamp values by one hour in BST timezone
'HDP--3.1.4,The table containing the parquet timestamp which has hourly data ,hive server is pushing the hour data into into next date example is shown below , please check before and after 29 th Mar 2020 , where Mar 29 is the BST time settings with day light saving' | 2020-03-22 | 2020-03-22 00:00:59.0 | 2020-03-22 23:59:59.0 | | 2020-03-23 | 2020-03-23 00:00:59.0 | 2020-03-23 23:59:59.0 | | 2020-03-24 | 2020-03-24 00:00:59.0 | 2020-03-24 23:59:59.0 | | 2020-03-25 | 2020-03-25 00:00:59.0 | 2020-03-25 23:59:59.0 | | 2020-03-26 | 2020-03-26 00:00:59.0 | 2020-03-26 23:59:59.0 | | 2020-03-27 | 2020-03-27 00:00:59.0 | 2020-03-27 23:59:59.0 | | 2020-03-28 | 2020-03-28 00:00:59.0 | 2020-03-28 23:59:59.0 | | 2020-03-29 | 2020-03-29 00:00:59.0 | 2020-03-30 00:59:59.0 | | 2020-03-30 | 2020-03-30 01:00:59.0 | 2020-03-31 00:59:59.0 | | 2020-03-31 | 2020-03-31 01:00:59.0 | 2020-04-01 00:59:59.0 | | 2020-04-01 | 2020-04-01 01:00:59.0 | 2020-04-02 00:59:59.0 | | 2020-04-02 | 2020-04-02 01:00:59.0 | 2020-04-03 00:59:59.0 |
When writing to parquet table in hive make sure the timestamp values are in UTC and set time zone in hive to match the local timezone . set time zone LOCAL; or set time zone '+1:00'
hql split time into intervals
I have a Hive table with some data and i would like to split it in to 15 minutes intervals et return the total call duration for every interval Hive Table example : ID Start End Total Duration 1 1502296261 1502325061 28800 My output should be shown as : ID Interval Duration 1 2017-08-09 18:30:00 839 1 2017-08-09 18:45:00 900 1 2017-08-09 19:00:00 900 ... 1 2017-08-10 02:15:00 900 1 2017-08-10 02:30:00 61 What is the best solution to do that in a efficient way ? Thanks.
This is the basic solution. The displayed timestamp (Interval) depends on your system timezone. with t as (select stack(1,1,1502296261,1502325061) as (`ID`,`Start`,`End`)) select t.`ID` as `ID` ,from_unixtime((t.`Start` div (15*60) + pe.pos)*(15*60)) as `Interval` , case when pe.pos = t.`End` div (15*60) - t.`Start` div (15*60) then t.`End` else (t.`Start` div (15*60) + pe.pos + 1)*(15*60) end - case when pe.pos = 0 then t.`Start` else (t.`Start` div (15*60) + pe.pos)*(15*60) end as `Duration` from t lateral view posexplode(split(space(int(t.`End` div (15*60) - t.`Start` div (15*60))),' ')) pe ; +----+---------------------+----------+ | id | interval | duration | +----+---------------------+----------+ | 1 | 2017-08-09 09:30:00 | 839 | | 1 | 2017-08-09 09:45:00 | 900 | | 1 | 2017-08-09 10:00:00 | 900 | | 1 | 2017-08-09 10:15:00 | 900 | | 1 | 2017-08-09 10:30:00 | 900 | | 1 | 2017-08-09 10:45:00 | 900 | | 1 | 2017-08-09 11:00:00 | 900 | | 1 | 2017-08-09 11:15:00 | 900 | | 1 | 2017-08-09 11:30:00 | 900 | | 1 | 2017-08-09 11:45:00 | 900 | | 1 | 2017-08-09 12:00:00 | 900 | | 1 | 2017-08-09 12:15:00 | 900 | | 1 | 2017-08-09 12:30:00 | 900 | | 1 | 2017-08-09 12:45:00 | 900 | | 1 | 2017-08-09 13:00:00 | 900 | | 1 | 2017-08-09 13:15:00 | 900 | | 1 | 2017-08-09 13:30:00 | 900 | | 1 | 2017-08-09 13:45:00 | 900 | | 1 | 2017-08-09 14:00:00 | 900 | | 1 | 2017-08-09 14:15:00 | 900 | | 1 | 2017-08-09 14:30:00 | 900 | | 1 | 2017-08-09 14:45:00 | 900 | | 1 | 2017-08-09 15:00:00 | 900 | | 1 | 2017-08-09 15:15:00 | 900 | | 1 | 2017-08-09 15:30:00 | 900 | | 1 | 2017-08-09 15:45:00 | 900 | | 1 | 2017-08-09 16:00:00 | 900 | | 1 | 2017-08-09 16:15:00 | 900 | | 1 | 2017-08-09 16:30:00 | 900 | | 1 | 2017-08-09 16:45:00 | 900 | | 1 | 2017-08-09 17:00:00 | 900 | | 1 | 2017-08-09 17:15:00 | 900 | | 1 | 2017-08-09 17:30:00 | 61 | +----+---------------------+----------+
Limitation of SQL Server recursive query
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 | ...