我需要为日期和时间之间的时段生成结果
select scheduleId,startDate,endDate,startTime,endTime,weekdayMask,
(CASE WHEN [weekdayMask] | 0x01 = [weekdayMask] THEN 1 ELSE 0 END) AS MONDAY,
(CASE WHEN [weekdayMask] | 0x02 = [weekdayMask] THEN 1 ELSE 0 END) AS TUESDAY,
(CASE WHEN [weekdayMask] | 0x04 = [weekdayMask] THEN 1 ELSE 0 END) AS WEDNESDAY,
(CASE WHEN [weekdayMask] | 0x08 = [weekdayMask] THEN 1 ELSE 0 END) AS THURSDAY,
(CASE WHEN [weekdayMask] | 0x10 = [weekdayMask] THEN 1 ELSE 0 END) AS FRIDAY,
(CASE WHEN [weekdayMask] | 0x20 = [weekdayMask] THEN 1 ELSE 0 END) AS SATURDAY,
(CASE WHEN [weekdayMask] | 0x40 = [weekdayMask] THEN 1 ELSE 0 END) AS SUNDAY
from otp.tutor_schedule
来自上述查询的结果
scheduleId startDate endDate startTime endTime weekdayMask MONDAY TUESDAY WEDNESDAY THURSDAY FRIDAY SATURDAY SUNDAY
----------- ---------- ---------- ---------------- ---------------- ----------- ----------- ----------- ----------- ----------- ----------- ----------- -----------
11 2017-01-01 2017-09-30 10:00:00.0000000 14:00:00.0000000 21 1 0 1 0 1 0 0
现在我需要为StartDate和endDate字段之间的工作日生成一小时的时间段。
SQL小提琴链接http://sqlfiddle.com/#!18/49f1b/1
任何人都可以帮助我
答案 0 :(得分:1)
也许一个递归的CTE会让你开始走正确的道路?
;WITH cte AS(
SELECT CAST('2018-01-01' AS DATETIME) AS StartDate
UNION ALL
SELECT DATEADD(HH, 1, StartDate)
FROM cte
WHERE StartDate < '2018-09-30'
)
SELECT *
FROM cte
OPTION (MAXRECURSION 7000)
答案 1 :(得分:1)
您可以使用以下内容。将您的原始查询放在第一个CTE
上(我硬编码了一些要测试的值)。
基本上我使用递归CTE
来生成每个开始日期和结束日期之间的所有小时数。然后根据每个工作日对每个计划过滤结果。第一个语句用于将DATEPART WEEKDAY
设置为已知值以便稍后进行比较。
SET DATEFIRST 1 -- 1: Monday, 7: Sunday
;WITH YourQueryResults AS
(
SELECT
scheduleId = 1,
startDate = CONVERT(DATE, '2017-01-01'),
endDate = CONVERT(DATE, '2017-09-30'),
startTime = CONVERT(TIME, '10:00:00.0000000'),
endTime = CONVERT(TIME, '14:00:00.0000000'),
MONDAY = 1,
TUESDAY = 0,
WEDNESDAY = 1,
THURSDAY = 0,
FRIDAY = 1,
SATURDAY = 0,
SUNDAY = 0
UNION ALL
SELECT
scheduleId = 2,
startDate = CONVERT(DATE, '2017-01-01'),
endDate = CONVERT(DATE, '2017-09-30'),
startTime = CONVERT(TIME, '17:00:00.0000000'),
endTime = CONVERT(TIME, '20:00:00.0000000'),
MONDAY = 1,
TUESDAY = 0,
WEDNESDAY = 0,
THURSDAY = 1,
FRIDAY = 0,
SATURDAY = 0,
SUNDAY = 0
),
RecursiveDatetimesByHour AS
(
SELECT
scheduleId = Y.scheduleId,
DatetimeByHour = CONVERT(DATETIME, Y.startDate) + CONVERT(DATETIME, Y.startTime),
WeekDay = DATEPART(WEEKDAY, CONVERT(DATETIME, Y.startDate) + CONVERT(DATETIME, Y.startTime))
FROM
YourQueryResults AS Y
UNION ALL
SELECT
scheduleId = R.scheduleId,
DatetimeByHour = DATEADD(HOUR, 1, R.DatetimeByHour),
WeekDay = DATEPART(WEEKDAY, DATEADD(HOUR, 1, R.DatetimeByHour))
FROM
RecursiveDatetimesByHour AS R
WHERE
DATEADD(HOUR, 1, R.DatetimeByHour) <
(SELECT CONVERT(DATETIME, Y.endDate) + CONVERT(DATETIME, Y.endTime) FROM YourQueryResults AS Y
WHERE Y.scheduleId = R.scheduleId)
)
SELECT
R.scheduleId,
R.DatetimeByHour,
R.WeekDay
FROM
RecursiveDatetimesByHour AS R
INNER JOIN YourQueryResults AS Y ON R.scheduleId = Y.scheduleId
WHERE
CONVERT(TIME, R.DatetimeByHour) BETWEEN Y.startTime AND Y.endTime AND
(
(Y.MONDAY = 1 AND R.WeekDay = 1) OR
(Y.TUESDAY = 1 AND R.WeekDay = 2) OR
(Y.WEDNESDAY = 1 AND R.WeekDay = 3) OR
(Y.THURSDAY = 1 AND R.WeekDay = 4) OR
(Y.FRIDAY = 1 AND R.WeekDay = 5) OR
(Y.SATURDAY = 1 AND R.WeekDay = 6) OR
(Y.SUNDAY = 1 AND R.WeekDay = 7)
)
OPTION
(MAXRECURSION 32000)
有些事情可以改进(例如避免做那么多演员表),但它可以让你很好地了解如何解决你的问题。
答案 2 :(得分:1)
请检查以下CTE查询
我在第一个CTE表达式dbo.DateTable
中看到了一个日历表对于时间,我的意思是我使用基本方法的时间;从spt_values读取 但是你也可以创建一个时间表
我希望它有所帮助
;with dt as (
select
convert(date, [date]) as [date],
DATEPART(dw, [date]) as wd,
case when DATEPART(dw, [date]) = 1 then 1 end as SUNDAY,
case when DATEPART(dw, [date]) = 2 then 1 end as MONDAY,
case when DATEPART(dw, [date]) = 3 then 1 end as TUESDAY,
case when DATEPART(dw, [date]) = 4 then 1 end as WEDNESDAY,
case when DATEPART(dw, [date]) = 5 then 1 end as THURSDAY,
case when DATEPART(dw, [date]) = 6 then 1 end as FRIDAY,
case when DATEPART(dw, [date]) = 7 then 1 end as SATURDAY
from [dbo].[DateTable]('2017-01-01', '2017-12-31') as dt
), tt as (
select convert(time, dateadd(hh,number-1,'00:00:00')) as [time]
from master..spt_values
where Type = 'P' and number between 1 and 25
), schedule as (
select
scheduleId,startDate,endDate,startTime,endTime,weekdayMask,
(CASE WHEN [weekdayMask] | 0x01 = [weekdayMask] THEN 1 ELSE 0 END) AS MONDAY,
(CASE WHEN [weekdayMask] | 0x02 = [weekdayMask] THEN 1 ELSE 0 END) AS TUESDAY,
(CASE WHEN [weekdayMask] | 0x04 = [weekdayMask] THEN 1 ELSE 0 END) AS WEDNESDAY,
(CASE WHEN [weekdayMask] | 0x08 = [weekdayMask] THEN 1 ELSE 0 END) AS THURSDAY,
(CASE WHEN [weekdayMask] | 0x10 = [weekdayMask] THEN 1 ELSE 0 END) AS FRIDAY,
(CASE WHEN [weekdayMask] | 0x20 = [weekdayMask] THEN 1 ELSE 0 END) AS SATURDAY,
(CASE WHEN [weekdayMask] | 0x40 = [weekdayMask] THEN 1 ELSE 0 END) AS SUNDAY
from tutor_schedule
), cte as (
select
dt.[date],
scheduleId,startDate,endDate,startTime,endTime,
ROW_NUMBER() over (partition by scheduleId, [date] order by [time]) as rn,
tt.[time]
from dt
left join schedule as s
on dt.[date] between s.startDate and s.endDate
left join tt
on tt.[time] >= s.startTime and tt.[time] < s.endTime
where
dt.MONDAY = s.MONDAY or
dt.TUESDAY = s.TUESDAY or
dt.WEDNESDAY = s.WEDNESDAY or
dt.THURSDAY = s.THURSDAY or
dt.FRIDAY = s.FRIDAY or
dt.SATURDAY = s.SATURDAY or
dt.SUNDAY = s.SUNDAY
)
select
scheduleId,
[date],
dateadd(hh, rn-1,startTime) as starttime,
dateadd(hh, rn,startTime) as endtime
from cte
where starttime <= [time]
我已使用以下示例数据进行了测试
create table tutor_schedule (
scheduleId int identity(1,1),
startDate date,
endDate date,
startTime time,
endTime time,
weekdayMask int
)
insert into tutor_schedule select '2017-01-01','2017-09-30','10:00:00','14:00:00',21
insert into tutor_schedule select '2017-01-05','2017-01-12','13:00:00','15:00:00',12