2009-11-16 28 views
2

我在這一點上有一點心理障礙。在sql服務器查詢(存儲過程)中按月分組的日期

我得到了預訂的酒店客房系統,它包含一個表作爲這樣

BookingRoomLink
BookingId(FK)
RoomId(FK)
START_DATE
END_DATE

我d喜歡查詢數據以提取每月的入住率。 我可以手動做到這一點(即過去一個月做這樣的事情)。

SELECT BookingRoomLink.Start_Date, 
     BookingRoomLink.End_Date, 
     DATEDIFF("d", BookingRoomLink.Start_Date, BookingRoomLink.End_Date) as RoomNights 
    FROM BookingRoomLink 
    WHERE BookingRoomLink.Start_Date >= dateadd(m, -1, getdate()) 
    AND BookingRoomLink.End_Date <= GETDATE() 

然後,我可以做這將使我「所用的」間夜,並減去這對在一個月內提供的住宿房間的結果或類似的計數。

例如, 10間客房 本月30天=可能有300晚的房間。 150使用(查詢結果)= 50%佔用率。

問題

Id喜歡這個自動化成存儲過程。

是否有可能在給定年份將其分組爲幾個月?

我該如何確保與一個月邊界重疊的預訂適合處理?

+0

樣本數據和預期結果總是有幫助! – JonH 2009-11-16 18:13:52

回答

3
WITH (
     SELECT 0 
     UNION ALL 
     SELECT m + 1 
     FROM mon 
     WHERE m < 11 
     ), 
     yr (y) AS 
     (
     SELECT CAST('1990-01-01' AS DATETIME) 
     UNION ALL 
     SELECT DATEADD(year, 1, y) 
     FROM yr 
     WHERE y <= GETDATE() 
     ), 
     dates (smy, emy) AS 
     (
     SELECT DATEADD(month, m, y), DATEADD(month, m + 1, y) 
     FROM yr 
     CROSS JOIN 
       mon 
     ), 
     diffs (smy, emy, days) AS 
     (
     SELECT smy, emy, DATEDIFF(day, smy, emy) 
     FROM dates 
     ) 
SELECT smy, 
     roomId, 
     CAST(SUM(DATEDIFF(day, 
     CASE WHEN start_date < smy THEN smy ELSE start_date END, 
     CASE WHEN end_date > emy THEN emy ELSE end_date END 
     )) AS FLOAT)/days 
FROM diffs 
JOIN bookings 
ON  start_date < emy 
     AND end_date >= smy 
GROUP BY 
     roomId, smy, emy, days 
+0

+1使用一個簡單的解決方案,並考慮到在第一天開始或在該月的最後一天結束後停留 – 2009-11-16 18:30:43

+0

sql 2008爲我提供這個 155,15級,狀態1,第19行 'mon'不是公認的dateadd選項。 Msg 174,Level 15,State 1,Line 30 datediff函數需要3個參數。 – Matt 2009-11-16 18:41:30

+0

'@ jossy':查看更新。我在樣本數據上進行了測試。 – Quassnoi 2009-11-16 20:00:32

0

您可以使用DATEPART函數獲取該號碼的月份編號和組。

0

如果您可以將計算列添加到表中,請將其值設置爲要使用的月份。我肯定會選擇「連續存儲」與每次計算。

至於跨月,你必須決定如何建模。如果您的預訂持續3個月,該怎麼辦? 4? 12?更多?

至於這個月,你可能會嘗試一個像200911這樣的6位數值,所以你可以很容易地對它們進行排序,但將它們保留在一個整數字段中。如果這個值被計算出來,那麼沒有人能夠接受它。

3

如果你需要經常這樣做,你可以添加這些年份和月份部分作爲持久化計算列的表格,並把它們的索引:

ALTER TABLE dbo.BookingRoomLink 
    ADD StartMonth AS MONTH(Start_Date) PERSISTED 

ALTER TABLE dbo.BookingRoomLink 
    ADD StartYear AS Year(Start_Date) PERSISTED 

ALTER TABLE dbo.BookingRoomLink 
    ADD EndMonth AS MONTH(End_Date) PERSISTED 

ALTER TABLE dbo.BookingRoomLink 
    ADD EndYear AS Year(End_Date) PERSISTED 

現在,您可以選擇這些新的計算列,在WHERE子句中使用它們,在這些列中使用它們 - 它們將始終基於Start_Date和End_Date進行更新 - 它們在每次訪問它們時都不計算 - >比在所有查詢中使用DATEPART快得多!

0

嘗試:

Select DateName(month, start_Date) + 
     DateName(Year, Start_Date) as MonthName, 
    Sum(DateDiff(Day, Start_Date, 
     Case DateDiff(Month, Start_Date, End_Date) 
      When 0 Then End_Date 
      Else DateAdd(day, -day(Start_Date), 
        DateAdd(day, DateDiff(day, 0, Start_Date), 0)) Bookings 
From BookingRoomLink 
Group By DateName(month, start_Date) + DateName(Year, Start_Date) 
2

你可以 「圓」 之日至本月1日,並就再GROUP BY。與使用DatePart類似,但您仍然有一個有效日期,因此您可以在執行分組之前或之後在WHERE子句中使用日期範圍。

 
SELECT [Date] = DATEADD(Month, DATEDIFF(Month, 0, Start_Date), 0), -- 1st of the month 
     [Bookings] = COUNT(*) 
FROM BookingRoomLink 
GROUP BY DATEADD(Month, DATEDIFF(Month, 0, Start_Date), 0) 
ORDER BY [Date] 
+0

我喜歡這個解決方案。簡單!我喜歡少輸入。謝謝。 – 2010-05-20 17:58:18