Сложный SQL Query совет: логика выделения резервирования - PullRequest
0 голосов
/ 27 июня 2011

Я пытаюсь сделать сложный запрос (по крайней мере, он сложный для меня) в 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);

Это значит это;

  • Отель с идентификатором 1 имеет 10 номеров на 2011-01-01
  • Отель с идентификатором 1 имеет 10 номеров на 2011-01-02
  • Отель с идентификатором 1 имеет 10 номеров на 2011-01-03

Затем, после этого, если мы получим бронирование между 2011-01-01 и 2011-01-03, как показано ниже;

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

Ситуация будет такой, как показано ниже;

  • Отель с идентификатором 1 имеет 9 номеров, оставшихся после бронирования 2011-01-01
  • Отель с идентификатором 1 имеет 9 номеров, оставшихся после бронирования 2011-01-02
  • Отель с идентификатором 1 имеет 10 выделение, оставшееся после бронирования 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

Так как мне этого добиться?

EDIT

Некоторые из них должны задаться вопросом, почему отвод отводится на первые два дня бронирования, а не на последний. Это потому, что гость не будет оставаться весь день в отеле в последний день. Он должен освободить комнату до 12:00. Таким образом, в последний день использования выделений не будет.

Ответы [ 2 ]

4 голосов
/ 27 июня 2011
;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 в качестве таблицы подсчета для получения списков дат вместо диапазонов дат. Затем расширенный список выделений объединяется с таблицей @Resevations. Для каждой даты в списке соответствующее выделение уменьшается на количество резервирований, диапазоны которых соответствуют данной дате.

2 голосов
/ 27 июня 2011

Я немного поспешно написал свое предложение where. Я не знал, хочешь ли ты разобраться с пустыми днями. вот что я придумал после установки условия where. Причина у меня datejumps состоит в том, чтобы компенсировать ограничение 100 рекурсивных вызовов в sql. Поэтому я объединяю 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...