2016-09-15 352 views
1

我有一个约会表,其中包含两个字段 - start_date和end_date,两个日期时间。表格中没有时间段的重叠。使用SQL查找可用空缺

给定一个特定的时间段(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

而给定的搜索参数开始= 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

+1

这是一个很好的开始。 http://spaghettidba.com/2015/04/24/how-to-post-a-t-sql-question-on-a-public-forum/鉴于您发布的内容,我们无法提供帮助。 –

+0

哪个版本的SQL? – SqlOnly

+0

你只是想显示两个日期之间的所有结果? – mfredy

回答

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

这很好用!我只是想知道从性能的角度来看,是否有更具成本效益的方式来实现它。参见例如http://blog.sqlauthority.com/2013/09/25/sql-server-how-to-access-the-previous-row-and-next-row-value-in- select-statement-part-4 /与示例2相比,但我不能让3和4与我的案例一起工作。 – user1480192

+0

@ user1480192我相信有一种方法。你确定要检查其他人的查询吗?通常有多种方式来完成单个任务,因此其他人可能会找到一种可能更有效的方法。我的直挺挺。我会研究它,看看我能不能做得更好。 – CodyMR

+0

@ user1480192您是否知道使用我的查询处理信息需要多长时间? – CodyMR

0
SELECT Start_Date, End_Date 
FROM TableName 
BETWEEN Start_Date AND End_Date 

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

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

+0

这是行不通的。他基本上希望在他的表中不存在的两个用户输入日期时间值('开始和结束')之间的时间。用'int'来想想它。在他的桌子上,他有'开始4,结束6'和'开始9,结束10'。如果他的用户输入'start,end'是'0,10',那么他希望'0,4 - 6,9'。 – CodyMR

0

我在这个问题上像Mfredy一样思考。但是我会在数据时间计算的基础上给出我的标准看法。

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

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

小心< vs < =。如果您正在过滤结束日期。例如说1月份的最后一天将被包括在内,但feb的第一天不是。最好是做< = 1/31。如果您使用< =进行比较,并且不会下降到02/01之前的最后一个毫秒,则可以切断记录。

0

我已经尝试重新创建您的场景,然后解决它。在一些地方,我为了这个例子简化了一些东西,但你应该能够推断它。

我相信也有更优雅的方式,但是一个起点,我很喜欢搞清楚它。

我想采取的方法是建立可用“时隙”

首先列表,我创建了一个表格,表格中预订

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

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

可用小时,然后做了相同分钟的间隔

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

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

,并再次相同(我用5周分钟的时间间隔为简单起见去),我想与

工作
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分钟,你应该能够根据是否是空闲块的开始/结束来决定最小值/最大值

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窗口函数来完成此操作。

+0

好奇。你错过了什么样的信息? – CodyMR