使用 SQL 查找空缺职位

Find available openings using SQL

我有一个 table 的约会记录有两个字段 - start_date 和 end_date,都是日期时间。 table.

中没有时间段重叠

给定一个特定时期(search_start 和 search_end),我需要使用 SQL.[=12 生成这些约会(从和到)之间的所有空缺列表=]

例如:给定 table 中的两个约会:

2016 年 9 月 15 日08:00至 2016 年 9 月 15 日09:00

2016 年 9 月 15 日10:00至 2016 年 9 月 15 日12:00

并且给定搜索参数 start= 2016 年 9 月 1 日 00:00 和 end= 2016 年 9 月 30 日 23:59,结果应该是

2016 年 9 月 1 日00:00至 2016 年 9 月 15 日08:00

2016 年 9 月 15 日09:00至 2016 年 9 月 15 日10:00

2016 年 9 月 15 日12:00至 2016 年 9 月 30 日23:59

这里是生成样本的脚本table:

CREATE TABLE [dbo].[Table_1](   
[from_date] [datetime] NOT NULL, 
[to_date] [datetime] NULL, 
CONSTRAINT [PK_Table_1] PRIMARY KEY CLUSTERED  ( [from_date] ASC ) 
WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
    ) ON [PRIMARY]

    GO

INSERT [dbo].[Table_1] ([from_date], [to_date]) VALUES (CAST(0x0000A6820083D600 AS DateTime), CAST(0x0000A682009450C0 AS DateTime))

INSERT [dbo].[Table_1] ([from_date], [to_date]) VALUES (CAST(0x0000A68200A4CB80 AS DateTime), CAST(0x0000A68200C5C100 AS DateTime))

我正在使用 MSSQL 2008 R2

SELECT Start_Date, End_Date
FROM TableName
BETWEEN Start_Date AND End_Date

这道题好像很简单??我不确定,如果我把你的问题简单化了?

你只需要确保,你也添加了时间位!

在这个问题上我的想法和 Mfredy 一样。但我会就数据时间计算的注意事项给出我的标准说明。

以下是一些常见问题。
一些(如 Mysql)sql 引擎以格林威治标准时间(英国的某个时区)存储数据。然后他们计算格林威治标准时间的小时数以获得实际时间。因此,如果您距格林威治标准时间 8 小时,如果您使用连接到 MySql 数据库的 MsSql 客户端,则可能会减少 8 小时。因为 MsSql 不理解 MySql 使用的 GMT 的这个 8 小时增量。您也可以在应用程序和操作系统中看到这一点。

另一个问题是您考虑的是日期,除非您将日期时间转换为日期,否则您必须确保考虑的是日期和时间。

注意 < 与 <=。如果您要过滤结束日期。比如说,一月的最后一天要包括在内,但二月的第一天不包括在内。做 < 02/01 比做 <= 1/31 更好。如果您使用 <= 进行比较并且不下降到 02/01 之前的最后一毫秒,您可能会切断记录。

我试过重新创建您的场景,然后解决它。为了举例,我在某些地方做了一些简化,但你应该能够推断出来。

我相信还有更优雅的方法,但这是一个起点,我很喜欢弄清楚它。

我想采用的方法是建立可用列表 'time slots'

首先,我创建了一个table,其中包含可用的预订时间

CREATE TABLE [dbo].[HOURS](
HourID [int] NULL) 

INSERT INTO dbo.[hours] VALUES (9),(10),(11),(12),(13),(14),(15),(16),(17)

然后对分钟间隔执行相同操作(为简单起见,我使用 5 分钟间隔)

CREATE TABLE [dbo].MINS(
MinID [int] NULL    ) 

INSERT INTO dbo.mins VALUES (5),(10),(15),(20),(25),(30),(35),(40),(45),(50),(55),(60)

我想合作的日期也是如此

CREATE TABLE [dbo].DATES(
DATES [Date] NULL) 

INSERT INTO dbo.DATES values
('20160901'),('20160902')

使用这些 table,我创建了一个列出所有可用的视图 'slots'

CREATE VIEW AllTimeSlots AS
SELECT 
cast(dates AS datetime) + cast(DATEADD(hour, hourid, DATEADD(minute, minid, DATEADD(second, 00, 0))) AS datetime) AS TimeSlot
FROM dbo.[hours] CROSS JOIN dbo.mins CROSS JOIN dates

然后我创建了一个 table 包含约会

CREATE TABLE [dbo].Appointments(
AppointmentStart [Datetime] NULL,
AppointmentEnd [DateTime] null
) 

INSERT INTO dbo.Appointments
VALUES
('20160901 10:40:00 AM','20160901 10:55 AM'),('20160901 01:00:00 PM','20160901 02:05:00 PM')

然后,我创建了另一个视图,将插槽和预订结合起来。记下我用来屏蔽两次之间所有槽的连接

CREATE VIEW SlotAvailability 
AS
SELECT TimeSlot,CASE WHEN AppointmentStart IS NULL THEN 'Free' ELSE 'Booked' END AS [Availability]
FROM (
SELECT Timeslot,apt.AppointmentStart, apt.AppointmentEnd FROM
 dbo.AllTimeSlots ats
LEFT OUTER JOIN  dbo.Appointments apt
 on ats.TimeSlot >= appointmentstart and ats.timeslot <= appointmentend) q1

正在做

Select Timeslot, [availability] from SlotAvailability where [availability] = 'free'

将列出所有可用的时间段。

最后一点,我还没有完全做到(运行 现在已经过时了)然后将其转换为每个 'free' 插槽的开始结束时间 - 已尝试几个方法,但没有破解它 - 我想如果你加入那个 table (视图)到它自己的时间段 + 5 分钟,你应该能够然后 min/max 值基于它是否是空闲块的开始/结束

我今天通常解决这个问题的方法是使用 APPLY 运算符将 table 连接到自身。问题中的信息不足,无法为您设计完整的查询,但一般模式如下:

SELECT *
FROM MyTable t1
OUTER APPLY ( 
    SELECT TOP 1 * 
    FROM MyTable t 
    WHERE t.KeyFields = t1.KeyFields
        AND t.SequenceField > t1.SequenceField
 ) t2

现在我可以将每一行直接与 WHERE 子句中的后续行进行比较,以便 WHERE 子句对其进行过滤以仅显示存在间隙的行。

您有时也可以通过 LAG or LEAD 窗口函数来完成此操作。

使用你的值我得到了你想要的输出:)

DECLARE @start datetime = '2016-09-01 00:00:00'
DECLARE @finish datetime = '2016-09-30 23:59:00'

WITH rn AS (SELECT *, ROW_NUMBER() OVER (ORDER BY start) AS rn FROM opening)
SELECT CASE WHEN r1.rn = 1 THEN @start
                           ELSE r2.finish
       END as START,
       CASE WHEN r1.rn IS NULL THEN @finish
                               ELSE r1.start
       END  AS FINISH
    FROM rn r1 
        FULL OUTER JOIN rn r2 
            ON r1.rn = r2.rn + 1
    WHERE ISNULL(r1.start, 0) != @start

opening 是你的 schedule/appointment table。 start 是您 table 中的开始日期,finish 是您 table 中的结束日期。 @start 是开始日期,@finish 是结束日期。您显然不需要使用 @start, @finish。我只是把它放在那里进行测试。