我正在尝试在SQL Server 2008上进行复杂的查询(至少对我来说很复杂),到目前为止,我可以走到这一步。这是代码;
DECLARE @Hotels AS TABLE(
HotelID INT,
HotelName NVARCHAR(100)
);
DECLARE @HotelAllotments AS TABLE(
HotelID INT,
StartDate DATETIME,
EndDate DATETIME,
Allotment INT
);
DECLARE @Reservations AS TABLE(
ReservationID INT,
HotelID INT,
CheckIn DATETIME,
CheckOut DATETIME,
IsCanceled BIT
);
INSERT @Hotels VALUES(1,'Foo Hotel');
INSERT @Hotels VALUES(2,'Poo Hotel');
INSERT @HotelAllotments VALUES(1,'2011-01-01', '2011-02-01', 10);
INSERT @HotelAllotments VALUES(1,'2011-02-02', '2011-02-18', 7);
INSERT @HotelAllotments VALUES(1,'2011-02-19', '2011-05-18', 19);
INSERT @HotelAllotments VALUES(1,'2011-05-19', '2011-10-18', 30);
INSERT @HotelAllotments VALUES(2,'2011-05-19', '2011-10-18', 30);
INSERT @Reservations VALUES(100, 1, '2011-05-10','2011-05-24',0);
INSERT @Reservations VALUES(101, 1, '2011-05-18','2011-05-28',0);
INSERT @Reservations VALUES(102, 1, '2011-03-07','2011-03-19',0);
INSERT @Reservations VALUES(103, 1, '2011-08-29','2011-09-07',0);
INSERT @Reservations VALUES(104, 1, '2011-09-01','2011-09-07',1);
INSERT @Reservations VALUES(105, 1, '2011-09-01','2011-09-07',1);
with e as(
SELECT ReservationID as resid1, CheckIn as chin1, 1 as lvl
FROM @Reservations res1
WHERE res1.HotelID = 1
UNION ALL
SELECT ReservationID as resid2, DATEADD(DAY,1,stall.chin1) as chin2, 1
FROM @Reservations res2
INNER JOIN e stall ON stall.chin1 < res2.CheckOut
WHERE stall.resid1 = res2.ReservationID
)
SELECT tb.chin1, SUM(lvl)
FROM e tb
GROUP BY tb.chin1
ORDER BY tb.chin1 DESC
在 @HotelAllotments 部分,您可以看到 start 和 end 日期。分配是每天进行的。我的意思是如果行如下所示;
INSERT @HotelAllotments VALUES(1,'2011-01-01', '2011-01-03', 10);
这意味着这个;
然后,如果我们收到2011-01-01和2011-01-03之间的预订,如下所示;
INSERT @Reservations VALUES(106, 1, '2011-01-01','2011-01-03',0);
情况如下;
上面,我创建了一些临时表并插入了一些假值,我尝试了一个查询。它让我到了某个地方(我不知道怎么称呼它。所以,如果你有一个 有机会运行查询,你会看到它到目前为止的地方)但不是我需要的地方。我需要的是那个;
我需要列出酒店签订协议的所有日期及收到预订后的左侧分配。这是一个例子;
HotelID Date Allotment
------- ---------- ---------
1 2011-01-01 9
1 2011-01-02 9
1 2011-01-03 10
1 2011-01-04 10
1 2011-01-05 10
那我怎么能实现这个目标呢?
修改
有些人应该想知道为什么在预订的前两天取消分配,而不是最后一天。这是因为客人最后一天不会在酒店住一整天。他/她应该把房间倒空到凌晨12:00。因此,在最后一天不会有任何分配用途。
答案 0 :(得分:4)
;WITH expanded AS (
SELECT
a.HotelID,
Date = DATEADD(DAY, v.number, a.StartDate),
a.Allotment
FROM @HotelAllotments a
INNER JOIN master..spt_values v ON v.type = 'P'
AND v.number BETWEEN 0 AND DATEDIFF(DAY, a.StartDate, a.EndDate)
),
filtered AS (
SELECT
e.HotelID,
e.Date,
Allotment = e.Allotment - COUNT(r.ReservationID)
FROM expanded e
LEFT JOIN @Reservations r ON e.HotelID = r.HotelID
AND e.Date >= r.CheckIn AND e.Date < r.CheckOut
AND r.IsCanceled = 0
GROUP BY e.HotelID, e.Date, e.Allotment
)
SELECT *
FROM filtered;
此解决方案使用系统表master..spt_values
作为tally table来获取日期列表而不是日期范围。接下来,扩展的分配列表与@Rivesvations表连接在一起。对于列表中的每个日期,相应的分配将减少其范围与给定日期匹配的预订数量。
答案 1 :(得分:2)
我在编写where where子句时有点草率。我不知道你是否想要解决这些空白日子。这是我在设置where子句后想出的。我有datejumps的原因是为了弥补sql中100个重复调用的限制。所以我从系统表中加入了10行,更好地利用了100次重复,这样我可以获得1000行而不是100行。
WITH cte(HOTELID, STARTDATE, ENDDATE, Allotment)
as
(
SELECT H.HOTELID, A.STARTDATE + RN STARTDATE, (SELECT MAX(ENDDATE) FROM @HotelAllotments) ENDDATE, (select Allotment from @HotelAllotments where A.STARTDATE + RN between StartDate and enddate and H.HOTELID = HOTELID) Allotment
FROM (
SELECT MIN(STARTDATE) STARTDATE from @HotelAllotments c
) A,
(SELECT TOP 10 rn = ROW_NUMBER() OVER (ORDER BY (SELECT 1))-1 FROM INFORMATION_SCHEMA.COLUMNS) B,
@Hotels H
UNION ALL
SELECT ch.HOTELID, ch.STARTDATE + 10, ENDDATE, (select Allotment from @HotelAllotments where CH.STARTDATE + 10 between StartDate and enddate and CH.HOTELID = HOTELID)
FROM cte ch
WHERE CH.STARTDATE< ENDDATE
AND CH.HOTELID = HOTELID
)
SELECT HotelID, StartDate Date , Allotment - (select count(*) from @Reservations where cte.STARTDATE between CheckIn and CheckOut and cte.HOTELID = HOTELID) Allotment
FROM CTE where allotment is not null
ORDER BY STARTDATE, HOTELID