2016-09-15 67 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