使用SQL

时间:2016-09-15 15:53:58

标签: sql sql-server schedule

我有一个约会表,其记录有两个字段 - start_date和end_date,都是datetime。表中的时间段没有重叠。

给定一个特定的句号(search_start和search_end),我需要使用SQL生成这些约会(来自和来自)之间所有空缺的列表。

例如:在表格中给出两个约会:

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和结束= 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

以下是生成示例表的脚本:

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

5 个答案:

答案 0 :(得分:1)

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

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是您的日程/约会表。 start是表格中的开始日期,finish是表格中的结束日期。 @start是开始日期,@finish是结束日期。你显然不需要使用@start, @finish。我只是把它放在那里进行测试。

答案 1 :(得分:0)

SELECT Start_Date, End_Date
FROM TableName
BETWEEN Start_Date AND End_Date

这个问题好像很容易?我不确定,如果我过分简化你的问题了吗?

你必须确保,你也加上时间位!

答案 2 :(得分:0)

我在这个问题上像Mfredy一样思考。但是,我将在数据时间计算方面给出我的标准内容。

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

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

提防< vs< =。如果您要过滤结束日期。比如说例如1月的最后一天是包括但是feb的第一天不是。最好做< 02/01它要做< = 1/31。如果使用< =进行比较,并且不要在02/01之前的最后一毫秒进行比较,则可以删除记录。

答案 3 :(得分:0)

我试图重新创建你的场景,然后解决它。在某些地方,我为了这个例子简化了一些事情,但你应该能够推断它。

我确信还有更优雅的方式,但这是一个起点,我非常喜欢搞清楚。

我想采取的方法是建立一个可用的'时间段'列表

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

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')

使用这些表格,我创建了一个列出所有可用“插槽”的视图

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

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

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'

将列出所有可用的时段。

最后一点,我还没有完成(并且暂时没时间)然后将其转换为每个“免费”插槽的开始时间 - 尝试了几种方法,但没有'破解它 - 我想如果你把那个表(视图)加到时间段+ 5分钟的本身,你应该能够根据它是否是一个空闲块的开始/结束来获得最小值/最大值

答案 4 :(得分:0)

我今天通常解决这个问题的方法是使用APPLY运算符将表连接到自身。问题中没有足够的信息来为您制作完整的查询,但这里是一般模式:

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子句将其过滤为仅显示存在间隙的行。

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