Я пытаюсь получить комбинацию начала и конца даты для непрерывных промежутков времени. Пролеты могут пересекать несколько строк, где конечная дата первой строки совпадает с датой окончания следующей строки. Предполагаемый результат - показать непрерывный диапазон дат с суммой отработанных часов для этого диапазона.SQL: найти непрерывные диапазоны дат для нескольких строк?
person startdate enddate hours
------ ----------------------- ----------------------- ------
5163 2013-04-29 07:00:00.000 2013-04-29 11:00:00.000 4.00
5163 2013-04-29 11:30:00.000 2013-04-29 15:30:00.000 4.00
5163 2013-04-29 15:30:00.000 2013-04-29 19:06:00.000 3.60
5851 2013-05-02 19:00:00.000 2013-05-02 23:00:00.000 4.00
5851 2013-05-02 23:00:00.000 2013-05-03 00:00:00.000 1.00
5851 2013-05-03 00:00:00.000 2013-05-03 00:31:00.000 0.52
Из приведенных выше данных я хочу следующее.
person startdate enddate hours
------ ----------------------- ----------------------- ------
5163 2013-04-29 07:00:00.000 2013-04-29 11:00:00.000 4.00
5163 2013-04-29 11:30:00.000 2013-04-29 19:06:00.000 7.60
5851 2013-05-02 19:00:00.000 2013-05-03 00:31:00.000 5.52
Для каждого человека и нового (прерывистых) промежутка дат, сравнить ENDDATE текущей строки, чтобы StartDate следующей строки. Если они совпадают, накапливайте часы и продолжайте обработку строк до тех пор, пока значение enddate/startdate не будет равным.
Среда SQL Server 2008 R2. Я попробовал запросы, связанные с self-join, используя функции row_number и partition(), но не смог получить успешное решение. Благодаря!
Редактировать: Вот поток данных для решения RichardTheKiwi - я запустил его для одного человека, чтобы узнать, сколько рекурсии создано за неделю ударов.
declare @startdate datetime;
set @startdate = '20130429';
declare @enddate datetime;
set @enddate = '20130506';
with tbl as (
select
PERSONNUM,
STARTDTM,
ENDDTM,
convert(decimal(10,2),1.0 * TIMEINSECONDS/3600) as timeinhours
from vp_totals
where paycodetype = 'p'
and applydate >= @startdate and APPLYDATE < @enddate
and (paycodename like '%regular%'
or paycodename like '%overtime%'
or PAYCODENAME like '%double time%')
and (PAYCODENAME not like '%shift premium%')
and PERSONNUM = 'loh-5851'
)
select * from tbl order by startdtm -- 27 rows
PERSONNUM STARTDTM ENDDTM timeinhours
LOH-5851 2013-04-29 14:30:00 2013-04-29 18:30:00 4.0000
LOH-5851 2013-04-29 19:00:00 2013-04-29 23:00:00 4.0000
LOH-5851 2013-04-29 23:00:00 2013-04-30 00:00:00 1.0000
LOH-5851 2013-04-30 00:00:00 2013-04-30 00:11:00 0.1800
LOH-5851 2013-04-30 14:45:00 2013-04-30 18:45:00 4.0000
LOH-5851 2013-04-30 19:15:00 2013-04-30 23:00:00 3.7500
LOH-5851 2013-04-30 23:00:00 2013-04-30 23:15:00 0.2500
LOH-5851 2013-04-30 23:15:00 2013-05-01 00:00:00 0.7500
LOH-5851 2013-05-01 00:00:00 2013-05-01 00:11:00 0.1800
LOH-5851 2013-05-01 14:30:00 2013-05-01 18:30:00 4.0000
LOH-5851 2013-05-01 19:00:00 2013-05-01 23:00:00 4.0000
LOH-5851 2013-05-01 23:00:00 2013-05-02 00:00:00 1.0000
LOH-5851 2013-05-02 00:00:00 2013-05-02 00:22:00 0.3700
LOH-5851 2013-05-02 14:30:00 2013-05-02 18:30:00 4.0000
LOH-5851 2013-05-02 19:00:00 2013-05-02 23:00:00 4.0000
LOH-5851 2013-05-02 23:00:00 2013-05-03 00:00:00 1.0000
LOH-5851 2013-05-03 00:00:00 2013-05-03 00:31:00 0.5200
LOH-5851 2013-05-03 14:45:00 2013-05-03 17:45:00 3.0000
LOH-5851 2013-05-03 17:45:00 2013-05-03 18:45:00 1.0000
LOH-5851 2013-05-03 19:15:00 2013-05-03 23:00:00 3.7500
LOH-5851 2013-05-03 23:00:00 2013-05-03 23:15:00 0.2500
LOH-5851 2013-05-03 23:15:00 2013-05-04 00:00:00 0.7500
LOH-5851 2013-05-04 00:00:00 2013-05-04 00:15:00 0.2500
LOH-5851 2013-05-04 14:00:00 2013-05-04 18:00:00 4.0000
LOH-5851 2013-05-04 18:30:00 2013-05-04 22:30:00 4.0000
LOH-5851 2013-05-04 22:30:00 2013-05-04 23:00:00 0.5000
LOH-5851 2013-05-04 23:00:00 2013-05-04 23:30:00 0.5000
,cte as (
select personnum, startdtm, enddtm, timeinhours
from tbl
union all
select t.personnum, cte.startdtm, t.enddtm, cast(cte.timeinhours + t.timeinhours as decimal(10,2))
from cte
join tbl t on cte.personnum = t.personnum and cte.enddtm = t.startdtm
)
select * from cte order by startdtm, timeinhours option (maxrecursion 32000) -- 52 rows
personnum startdtm enddtm timeinhours
LOH-5851 2013-04-29 14:30:00 2013-04-29 18:30:00 4.0000
LOH-5851 2013-04-29 19:00:00 2013-04-29 23:00:00 4.0000
LOH-5851 2013-04-29 19:00:00 2013-04-30 00:00:00 5.0000
LOH-5851 2013-04-29 19:00:00 2013-04-30 00:11:00 5.1800
LOH-5851 2013-04-29 23:00:00 2013-04-30 00:00:00 1.0000
LOH-5851 2013-04-29 23:00:00 2013-04-30 00:11:00 1.1800
LOH-5851 2013-04-30 00:00:00 2013-04-30 00:11:00 0.1800
LOH-5851 2013-04-30 14:45:00 2013-04-30 18:45:00 4.0000
LOH-5851 2013-04-30 19:15:00 2013-04-30 23:00:00 3.7500
LOH-5851 2013-04-30 19:15:00 2013-04-30 23:15:00 4.0000
LOH-5851 2013-04-30 19:15:00 2013-05-01 00:00:00 4.7500
LOH-5851 2013-04-30 19:15:00 2013-05-01 00:11:00 4.9300
LOH-5851 2013-04-30 23:00:00 2013-04-30 23:15:00 0.2500
LOH-5851 2013-04-30 23:00:00 2013-05-01 00:00:00 1.0000
LOH-5851 2013-04-30 23:00:00 2013-05-01 00:11:00 1.1800
LOH-5851 2013-04-30 23:15:00 2013-05-01 00:00:00 0.7500
LOH-5851 2013-04-30 23:15:00 2013-05-01 00:11:00 0.9300
LOH-5851 2013-05-01 00:00:00 2013-05-01 00:11:00 0.1800
LOH-5851 2013-05-01 14:30:00 2013-05-01 18:30:00 4.0000
LOH-5851 2013-05-01 19:00:00 2013-05-01 23:00:00 4.0000
LOH-5851 2013-05-01 19:00:00 2013-05-02 00:00:00 5.0000
LOH-5851 2013-05-01 19:00:00 2013-05-02 00:22:00 5.3700
LOH-5851 2013-05-01 23:00:00 2013-05-02 00:00:00 1.0000
LOH-5851 2013-05-01 23:00:00 2013-05-02 00:22:00 1.3700
LOH-5851 2013-05-02 00:00:00 2013-05-02 00:22:00 0.3700
LOH-5851 2013-05-02 14:30:00 2013-05-02 18:30:00 4.0000
LOH-5851 2013-05-02 19:00:00 2013-05-02 23:00:00 4.0000
LOH-5851 2013-05-02 19:00:00 2013-05-03 00:00:00 5.0000
LOH-5851 2013-05-02 19:00:00 2013-05-03 00:31:00 5.5200
LOH-5851 2013-05-02 23:00:00 2013-05-03 00:00:00 1.0000
LOH-5851 2013-05-02 23:00:00 2013-05-03 00:31:00 1.5200
LOH-5851 2013-05-03 00:00:00 2013-05-03 00:31:00 0.5200
LOH-5851 2013-05-03 14:45:00 2013-05-03 17:45:00 3.0000
LOH-5851 2013-05-03 14:45:00 2013-05-03 18:45:00 4.0000
LOH-5851 2013-05-03 17:45:00 2013-05-03 18:45:00 1.0000
LOH-5851 2013-05-03 19:15:00 2013-05-03 23:00:00 3.7500
LOH-5851 2013-05-03 19:15:00 2013-05-03 23:15:00 4.0000
LOH-5851 2013-05-03 19:15:00 2013-05-04 00:00:00 4.7500
LOH-5851 2013-05-03 19:15:00 2013-05-04 00:15:00 5.0000
LOH-5851 2013-05-03 23:00:00 2013-05-03 23:15:00 0.2500
LOH-5851 2013-05-03 23:00:00 2013-05-04 00:00:00 1.0000
LOH-5851 2013-05-03 23:00:00 2013-05-04 00:15:00 1.2500
LOH-5851 2013-05-03 23:15:00 2013-05-04 00:00:00 0.7500
LOH-5851 2013-05-03 23:15:00 2013-05-04 00:15:00 1.0000
LOH-5851 2013-05-04 00:00:00 2013-05-04 00:15:00 0.2500
LOH-5851 2013-05-04 14:00:00 2013-05-04 18:00:00 4.0000
LOH-5851 2013-05-04 18:30:00 2013-05-04 22:30:00 4.0000
LOH-5851 2013-05-04 18:30:00 2013-05-04 23:00:00 4.5000
LOH-5851 2013-05-04 18:30:00 2013-05-04 23:30:00 5.0000
LOH-5851 2013-05-04 22:30:00 2013-05-04 23:00:00 0.5000
LOH-5851 2013-05-04 22:30:00 2013-05-04 23:30:00 1.0000
LOH-5851 2013-05-04 23:00:00 2013-05-04 23:30:00 0.5000
,cte2 as (
select *, rn = row_number() over (partition by personnum, enddtm order by startdtm)
from cte
)
select * from cte2 order by startdtm, rn -- 52 rows
personnum startdtm enddtm timeinhours rn
LOH-5851 2013-04-29 14:30:00 2013-04-29 18:30:00 4.0000 1
LOH-5851 2013-04-29 19:00:00 2013-04-29 23:00:00 4.0000 1
LOH-5851 2013-04-29 19:00:00 2013-04-30 00:00:00 5.0000 1
LOH-5851 2013-04-29 19:00:00 2013-04-30 00:11:00 5.1800 1
LOH-5851 2013-04-29 23:00:00 2013-04-30 00:11:00 1.1800 2
LOH-5851 2013-04-29 23:00:00 2013-04-30 00:00:00 1.0000 2
LOH-5851 2013-04-30 00:00:00 2013-04-30 00:11:00 0.1800 3
LOH-5851 2013-04-30 14:45:00 2013-04-30 18:45:00 4.0000 1
LOH-5851 2013-04-30 19:15:00 2013-04-30 23:00:00 3.7500 1
LOH-5851 2013-04-30 19:15:00 2013-04-30 23:15:00 4.0000 1
LOH-5851 2013-04-30 19:15:00 2013-05-01 00:11:00 4.9300 1
LOH-5851 2013-04-30 19:15:00 2013-05-01 00:00:00 4.7500 1
LOH-5851 2013-04-30 23:00:00 2013-05-01 00:00:00 1.0000 2
LOH-5851 2013-04-30 23:00:00 2013-05-01 00:11:00 1.1800 2
LOH-5851 2013-04-30 23:00:00 2013-04-30 23:15:00 0.2500 2
LOH-5851 2013-04-30 23:15:00 2013-05-01 00:11:00 0.9300 3
LOH-5851 2013-04-30 23:15:00 2013-05-01 00:00:00 0.7500 3
LOH-5851 2013-05-01 00:00:00 2013-05-01 00:11:00 0.1800 4
LOH-5851 2013-05-01 14:30:00 2013-05-01 18:30:00 4.0000 1
LOH-5851 2013-05-01 19:00:00 2013-05-01 23:00:00 4.0000 1
LOH-5851 2013-05-01 19:00:00 2013-05-02 00:00:00 5.0000 1
LOH-5851 2013-05-01 19:00:00 2013-05-02 00:22:00 5.3700 1
LOH-5851 2013-05-01 23:00:00 2013-05-02 00:22:00 1.3700 2
LOH-5851 2013-05-01 23:00:00 2013-05-02 00:00:00 1.0000 2
LOH-5851 2013-05-02 00:00:00 2013-05-02 00:22:00 0.3700 3
LOH-5851 2013-05-02 14:30:00 2013-05-02 18:30:00 4.0000 1
LOH-5851 2013-05-02 19:00:00 2013-05-02 23:00:00 4.0000 1
LOH-5851 2013-05-02 19:00:00 2013-05-03 00:00:00 5.0000 1
LOH-5851 2013-05-02 19:00:00 2013-05-03 00:31:00 5.5200 1
LOH-5851 2013-05-02 23:00:00 2013-05-03 00:31:00 1.5200 2
LOH-5851 2013-05-02 23:00:00 2013-05-03 00:00:00 1.0000 2
LOH-5851 2013-05-03 00:00:00 2013-05-03 00:31:00 0.5200 3
LOH-5851 2013-05-03 14:45:00 2013-05-03 17:45:00 3.0000 1
LOH-5851 2013-05-03 14:45:00 2013-05-03 18:45:00 4.0000 1
LOH-5851 2013-05-03 17:45:00 2013-05-03 18:45:00 1.0000 2
LOH-5851 2013-05-03 19:15:00 2013-05-03 23:00:00 3.7500 1
LOH-5851 2013-05-03 19:15:00 2013-05-03 23:15:00 4.0000 1
LOH-5851 2013-05-03 19:15:00 2013-05-04 00:00:00 4.7500 1
LOH-5851 2013-05-03 19:15:00 2013-05-04 00:15:00 5.0000 1
LOH-5851 2013-05-03 23:00:00 2013-05-04 00:15:00 1.2500 2
LOH-5851 2013-05-03 23:00:00 2013-05-04 00:00:00 1.0000 2
LOH-5851 2013-05-03 23:00:00 2013-05-03 23:15:00 0.2500 2
LOH-5851 2013-05-03 23:15:00 2013-05-04 00:00:00 0.7500 3
LOH-5851 2013-05-03 23:15:00 2013-05-04 00:15:00 1.0000 3
LOH-5851 2013-05-04 00:00:00 2013-05-04 00:15:00 0.2500 4
LOH-5851 2013-05-04 14:00:00 2013-05-04 18:00:00 4.0000 1
LOH-5851 2013-05-04 18:30:00 2013-05-04 22:30:00 4.0000 1
LOH-5851 2013-05-04 18:30:00 2013-05-04 23:00:00 4.5000 1
LOH-5851 2013-05-04 18:30:00 2013-05-04 23:30:00 5.0000 1
LOH-5851 2013-05-04 22:30:00 2013-05-04 23:30:00 1.0000 2
LOH-5851 2013-05-04 22:30:00 2013-05-04 23:00:00 0.5000 2
LOH-5851 2013-05-04 23:00:00 2013-05-04 23:30:00 0.5000 3
select personnum, startdtm, max(enddtm) enddtm, max(timeinhours) timeinhours
from cte2
where rn=1
group by personnum, startdtm
order by personnum, startdtm
option (maxrecursion 32000) -- 12 rows
personnum startdtm enddtm timeinhours
LOH-5851 2013-04-29 14:30:00 2013-04-29 18:30:00 4.0000
LOH-5851 2013-04-29 19:00:00 2013-04-30 00:11:00 5.1800
LOH-5851 2013-04-30 14:45:00 2013-04-30 18:45:00 4.0000
LOH-5851 2013-04-30 19:15:00 2013-05-01 00:11:00 4.9300
LOH-5851 2013-05-01 14:30:00 2013-05-01 18:30:00 4.0000
LOH-5851 2013-05-01 19:00:00 2013-05-02 00:22:00 5.3700
LOH-5851 2013-05-02 14:30:00 2013-05-02 18:30:00 4.0000
LOH-5851 2013-05-02 19:00:00 2013-05-03 00:31:00 5.5200
LOH-5851 2013-05-03 14:45:00 2013-05-03 18:45:00 4.0000
LOH-5851 2013-05-03 19:15:00 2013-05-04 00:15:00 5.0000
LOH-5851 2013-05-04 14:00:00 2013-05-04 18:00:00 4.0000
LOH-5851 2013-05-04 18:30:00 2013-05-04 23:30:00 5.0000
Запрос отлично работает для небольших объемов данных, но при запуске для ожидаемого населения работника за период заработной платы (обычно одна недели), появляется уродливого Max рекурсии сообщений об ошибке.
Редактировать редактировать: см. Комментарии к исправлению Ричарда для проблемы рекурсии.
Привет Ричард, спасибо за это решение! Я хочу отметить это как ответ ... Хотя он работает с данными примера, при запуске над производственными данными я получаю сообщение об ошибке «Оператор завершен. Максимальная рекурсия 100 исчерпана до завершения заявки». Даже получил ошибку после использования подсказки «option (maxrecursion 32000)». Производственные данные составляют около 600 строк, и я ожидаю до 1500 строк для запроса. Есть ли альтернативный метод, который работает вокруг проблемы рекурсии? – user2391335
Я думаю, что у вас есть запись, где дата окончания = дата начала. Это единственное, о чем я могу думать (на данный момент), что приведет к бесконечной рекурсии. Вы можете предотвратить это, используя 'from cte join tbl t на cte.person = t.person и cte.enddate = t.startdate и t.enddate! = T.startdate' – RichardTheKiwi
Еще раз спасибо, Ричард! В предлагаемом изменении исправляется проблема рекурсии, и результат для моего образца выглядит правильно. – user2391335