2011-06-27 10 views
0

SQL Server 2008で複雑なクエリを実行しようとしています(少なくとも私にとっては複雑です)。ここにコードがあります。 @HotelAllotmentsセクションで複雑なSQLクエリのアドバイス:予約割り当てロジック

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 

開始終了日は、あなたが見ることができるようにあります。割当は毎日行われます。私は行が以下のようなものであれば意味します。

INSERT @HotelAllotments VALUES(1,'2011-01-01', '2011-01-03', 10); 

これはこれを意味します。

  • IDが1である2011-01-01
  • IDが1である2011-01-02
  • IDがある1 10を有しているホテルの10割当を持つホテルの10割当を持つホテル割当on 2011-01-03

その後、2011-01-01と2011-01-03の間に予約を受けた場合、以下のようになります。

INSERT @Reservations VALUES(106, 1, '2011-01-01','2011-01-03',0); 

状況は以下の通りです。

  • IDが1であるホテルの予約後に残る9割当てが2011-01-01
  • にIDが1であるホテル9割当が2011-01-02
  • に予約した後に残っているましたidが1であるホテルが2011-01-03以上

に予約した後に残され割当を持って、私はいくつかの一時テーブルを作成し、いくつかの偽の値を挿入し、私は、クエリを試してみました。私はどこかで私を取得します(私はそれを呼び出す方法がわかりません。クエリーを実行する機会が の場合、必要な場所ではありません)。私がここに必要なのはそれです。

私はホテルが契約を結んでいるすべての日付と、受け取った予約後にその左の割り当てをリストする必要があります。ここに例があります。

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 

これはどのように達成できますか?いくつかの彼らは最後の割当は、予約の最初の2日間奪われている理由だろうが、ないはず

EDIT

。最終日にホテルに一日宿泊していないからです。 S /彼は午前12時まで部屋を空にすべきです。したがって、最後の日に割り当ての使用はありません。

+1

私はちょうどタイプミスを願っています、2011-01-01 - > 2011-03-01は3ヶ月ではなく2ヶ月と1日です。問題のいくつかの点では、あなたはyyyy-mm-dd(誰もが期待している)とyyyy-dd-mm(これはほんのわずかです)を転置しているようです。 –

+0

@Aaron非常に良いキャッチ。それはちょうどタイプミスでした、それを修正しました。 – tugberk

+1

私は、次のセクションではまだタイプミスがあると思います。「その後、それを受け取ったら...」同じ第1弾と第3弾は、同じ日付の異なる割り当てを示します(前のセクションの日付と一致しません) 。また、予約の最初の2日間は割当が取られた理由を説明するのは良いでしょうが、最後のものではありません。同じ予約にもっと日数があるからですか?だから予約で最後の日を除く毎日から配当が差し引かれますか?次の日に別の予約が始まる場合はどうなりますか? –

答えて

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; 

この溶液は、日付の代わりに、日付範囲のリストを取得するためにtally tableとして、master..spt_values、システム・テーブルを使用します。次に、展開された割当リストが@Resevationsテーブルと結合されます。リスト内のすべての日付について、対応する割り当ては、その範囲が指定された日付と一致する予約の数だけ減少する。

+0

同じこと、私は今日後で家でそれをチェックし、あなたに知らせるつもりです。 – tugberk

+0

私はあなたのスクリプトをテストしていませんが、それは正しいと思われ、ちょうどあなたがポイントを値する難しい質問を解決するために、通常人々は固い質問のポイントに粘着しています。だからそれは十分に価値があり、あなたはもっと値する必要があります。誰かが解決策を見出す前に、この質問は数時間にわたって掲載されました。 –

+0

あなたのスクリプトは魅力的に機能しました。あなたの努力のすべてに感謝します。あなたのコードを調べるときに、 'master..spt_values'を使うロジックに問題があります。その役割は正確にここにあります。(私はあなたがそれを説明し、リンクも与えたが、私はまだそれを並べ替えるのに苦労している) – tugberk

2

私はwhere句を書いて少し面白かったです。私はあなたが空白の日を整理するかどうかを知りませんでした。ここでは、where節を設定した後に思いついたことがあります。私がdatejumpsを持っている理由は、SQLで100回の再帰呼び出しの制限を補うことです。だから、私はシステムテーブルから10行に参加して、100の代わりに100行を得ることができるように、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 
+0

私は今日後でそれを家で確認してお知らせします。 – tugberk

+0

申し訳ありませんが、私が望んでいたものではありません。しかし、努力のおかげで。 – tugberk

+2

私はこれをテストしたとき、私のものより多くの行を返しました(間違っていると思います)。また、あなたは今のところIsCanceledを考慮しておらず、予約範囲と比較すると、 OPが具体的に要求したとおり、他の点では、あなたのクエリはかなり優れていました(やはり!)、それは私を最も驚かせるものです。私はどういう意味ですか? :)しかし、私はすでに私の解決策について何が改善できるかについていくつか考えました。私はおそらく明日まで自分の考えを確認できるまで待つ必要があるでしょう。 –