【问题标题】:SQL Filter date range querySQL过滤日期范围查询
【发布时间】:2014-02-22 15:29:44
【问题描述】:

我有一个日程记录列表

工作人员时间表

其中一些已被预订。

预定时间表

还有一些假期

节假日

我只想获取可用的员工时间表

Expected Schedules= StaffSchedule - BookedSchedule-Holidays

即,我只想要 StaffSchedule 表中的第 2,6 行

以下是我尝试过的查询,但没有显示任何结果

with NonBookingSlots as
(
select StartdateTime,EndDateTime from Holidays
union all
select StartdateTime,EndDateTime from BookedSchedules
)

SELECT
    StaffId, StartdateTime, EndDateTime
FROM StaffSchedule
WHERE 
not exists (select 1
                from NonBookingSlots h
                where cast(StartdateTime as DATETIME) between 
                cast(h.startdatetime as DATETIME) 
                and cast(h.enddatetime as DATETIME)
           )

SQL FIDDLE DEMO

【问题讨论】:

  • 完整问题 +1
  • 员工的日程安排可以部分预订还是全部预订(与您的所有示例数据一样)?
  • @ShWiVeL,已订满

标签: sql sql-server sql-server-2008 sql-server-2008-r2


【解决方案1】:

对于所有示例,我都假设BookedSchedules 中的开始和结束时间将与StaffSchedules 的开始和结束时间完全匹配。

使用 CTE,类似问题:

我不建议使用此查询,但它可能会有所帮助,因为它类似于问题中的查询。它不是很可读。

with NonBookingSlots as
(
  select null as StaffId,StartdateTime,EndDateTime from Holidays
  union all
  select StaffId,StartdateTime,EndDateTime from BookedSchedules
)

select
  StaffId, StartdateTime, EndDateTime
from 
  StaffSchedule
where
  not exists(
    select
      1
    from 
      NonBookingSlots
    where
      StaffSchedule.StaffId = isnull(NonBookingSlots.StaffId,StaffSchedule.StaffId)
      and (
        (
          StaffSchedule.StartDateTime =  NonBookingSlots.StartDateTime
          and StaffSchedule.EndDateTime = NonBookingSlots.EndDateTime
        ) or (
          StaffSchedule.StartDateTime <  NonBookingSlots.EndDateTime
          and StaffSchedule.EndDateTime > NonBookingSlots.StartDateTime
        )
     )
  ) 

SQL 小提琴:http://sqlfiddle.com/#!3/9cbf4/14

无 CTE:

我认为这个版本更具可读性。

select
  StaffId, StartdateTime, EndDateTime
from 
  StaffSchedule
where
  not exists(
    select
      1
    from 
      BookedSchedules
    where
      StaffSchedule.StaffId = BookedSchedules.StaffId
      and StaffSchedule.StartDateTime =  BookedSchedules.StartDateTime
      and StaffSchedule.EndDateTime = BookedSchedules.EndDateTime
  ) and not exists(
    select
      1
    from 
      Holidays
    where
      StaffSchedule.StartDateTime <  Holidays.EndDateTime
      and StaffSchedule.EndDateTime > Holidays.StartDateTime
  )

SQL 小提琴:http://sqlfiddle.com/#!3/9cbf4/15

使用外键 - 我的建议:

如果BookedSchedules 始终与StaffSchedule 匹配,则应使用StaffSchedule 的外键,而不是复制BookedSchedules 中的开始和结束时间。这会产生更清晰、更高效的查询。

select
  StaffId, StartdateTime, EndDateTime
from 
  StaffSchedule
where
  not exists(
    select
      1
    from 
      BookedSchedules
    where
      StaffSchedule.Id = BookedSchedules.StaffScheduleId
  ) and not exists(
    select
      1
    from 
      Holidays
    where
      StaffSchedule.StartDateTime <=  Holidays.EndDateTime
      and StaffSchedule.EndDateTime >= Holidays.StartDateTime
  )

SQL 小提琴:http://sqlfiddle.com/#!3/8a684/3

【讨论】:

  • 我们不能像我用 CTE 尝试过的那样用单一的地方来制作它吗?
  • @Billa 你的意思是你只想要一个 EXISTS 子查询吗?
  • 我看不出在这种情况下使用 with 子句减少 EXISTS 子查询的数量(从 2 到 1)有什么意义。您不会多次引用它(使用 with 子句的一个优点),并且您根本不会简化代码(使用 with 子句的另一个优点)。您是否有特殊原因要使用一个存在子查询和一个 with 子句,而不是没有 with 子句和 2 个存在子查询?
  • @ShWiVeL 我同意,它似乎只会让它变得更复杂。我以这种方式展示了@Billa 提出问题的方式。我建议重构并在BookedSchedules 中使用StaffSchedule 的外键。
  • @Billa 我更新了答案以包括几个替代方案。他们都得到了你正在寻找的结果;但是,我建议使用外键。
【解决方案2】:
select *
  from dbo.StaffSchedule x
 where not exists (select 'y'
          from dbo.BookedSchedules y
         where y.staffid = x.staffid
           and y.startdatetime = x.startdatetime
           and y.enddatetime = x.enddatetime)
   and not exists (select 'z'
          from dbo.Holidays z
         where cast(z.startdatetime as date) =
               cast(x.startdatetime as date))

这里的 SQL 小提琴:http://sqlfiddle.com/#!3/07698/19/0

编辑 - 处理超过一天的假期:

select *
  from dbo.StaffSchedule x
 where not exists (select 'y'
          from dbo.BookedSchedules y
         where y.staffid = x.staffid
           and y.startdatetime = x.startdatetime
           and y.enddatetime = x.enddatetime)
   and not exists (select 'z'
          from dbo.Holidays z
         where cast(z.startdatetime as date) <=
               cast(x.startdatetime as date)
           and cast(z.enddatetime as date) >=
               cast(x.enddatetime as date))

【讨论】:

  • 假期可以是范围。前 3 月 5 日 - 3 月 7 日
  • @Billa 为持续 2 天以上的假期添加了逻辑
猜你喜欢
  • 2011-05-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多