2009-11-16 2 views
2

Im이 항목에 정신 블록이 약간 있습니다.SQL 서버 쿼리 (저장 프로 시저)에서 월 단위로 그룹화

나는 호텔 객실에 대한 시스템을 예약있어 그것은 테이블을 포함 그래서 BookingRoomLink가
BookingId (FK)가
RoomId (FK)
START_DATE
END_DATE

나 '

매월 점유 수준을 추출하기 위해 데이터를 쿼리하고 싶습니다. 나는 이것을 수동으로 할 수있다 (예를 들면, 지난 달 동안 이런 일을한다).

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 실 x 30 일 = 300 가능한 객실 숙박이 가능합니다. 150 사용 (쿼리 결과) = 50 % 점유.

문제

ID는 저장 프로 시저에이를 자동화하고 싶다.

특정 연도에 이것을 개월 단위로 그룹화 할 수 있습니까?

월 경계선과 겹치는 예약이 적절하게 처리되도록하려면 어떻게해야합니까?

+0

샘플 데이터 및 예상 결과는 항상 도움이됩니다. – JonH

답변

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 간단한 솔루션을 사용하고 또한 월 말일 이후에 끝나는 일 또는 종료일을 고려하십시오 –

+0

sql 2008이 저에게 제공합니다 메시지 155, 수준 15, 상태 1, 줄 19 'mon' 인식 된 dateadd 옵션이 아닙니다. 메시지 174, 수준 15, 상태 1, 줄 30 datediff 함수에는 3 개의 인수가 필요합니다. – Matt

+0

'@ jossy' : 업데이트를 참조하십시오. 샘플 데이터를 테스트했습니다. – Quassnoi

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 

는 이제 새로운 계산 열을 선택할 수를 GROUP WHERE 절, GROUP에서 해당 열을 사용하십시오. Start_Date 및 End_Date에 따라 항상 업데이트됩니다. 액세스 할 때마다 계산되지는 않습니다.> 모든 쿼리에서 DATEPART을 사용하는 것보다 훨씬 빠릅니다. !

0

시도 : 그에있는 달의 1 일 후 GROUP BY에

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

당신은 할 수 있었다 "라운드"날짜.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

나는이 해결책을 좋아했다. 단순한! 나는 덜 입력하고 싶습니다. 고맙습니다. –

관련 문제