SQL查询在开始时间和结束时间之间生成一小时时隙

时间:2018-03-19 14:07:31

标签: sql-server

我需要为日期和时间之间的时段生成结果

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

任何人都可以帮助我

3 个答案:

答案 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