我有一个约会表,其记录有两个字段 - 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
答案 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)