какой самый простой способ рассчитать цену для гостиничного номера, если пользователь запрашивает общую цену между 5/1/2015
до 25/1/2015
.
но ничто из этого не имеет для меня никакого смысла.
Я попробовал пару запросов, но похоже, что стрелка вслепую стреляет. Может ли кто-нибудь предложить мне простой и элегантный способ сделать это?
Ответ 3
Это легко обрабатывать, если у вас есть существующая таблица дат для работы. У вас его уже нет? Ниже вы найдете две функции, которые помогут вам начать работу. Вот как вы их используете:
-- Arguments can be passed in any order
SELECT * FROM dbo.RangeDate('2015-12-31', '2015-01-01');
SELECT * FROM dbo.RangeSmallInt(10, 0);
SELECT A.HID, SUM(A.PRICE_PER_DAY)
FROM dbo.RangeDate('2000-01-01', '2020-12-31') Calendar
JOIN HotelRate A
ON Calendar.D BETWEEN A.START_DATE and A.END_DATE
GROUP BY A.HID;
Вы можете использовать функцию RangeDate в качестве календаря, или вы можете использовать ее для создания собственной функции/таблицы календаря.
-- Generate a range of up to 65,536 contiguous DATES
CREATE FUNCTION dbo.RangeDate (
@date1 DATE = NULL
, @date2 DATE = NULL
)
RETURNS TABLE
AS
RETURN (
SELECT D = DATEADD(DAY, A.N, CASE WHEN @date1 <= @date2 THEN @date1 ELSE @date2 END)
FROM dbo.RangeSmallInt(
CASE WHEN @date1 IS NOT NULL AND @date2 IS NOT NULL THEN 0 END
, ABS(DATEDIFF(DAY, @date1, @date2))
) A
);
-- Generate a range of up to 65,536 contiguous BIGINTS
CREATE FUNCTION dbo.RangeSmallInt (
@n1 BIGINT = NULL
, @n2 BIGINT = NULL
)
RETURNS TABLE
AS
RETURN (
WITH Numbers AS (
SELECT N FROM(VALUES
(1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 16
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 32
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 48
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 64
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 80
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 96
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 112
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 128
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 144
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 160
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 176
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 192
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 208
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 224
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 240
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 256
) V (N)
)
SELECT TOP (
CASE
WHEN @n1 IS NOT NULL AND @n2 IS NOT NULL THEN ABS(@n2 - @n1) + 1
ELSE 0
END
)
N = ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) - 1 + CASE WHEN @n1 <= @n2 THEN @n1 ELSE @n2 END
FROM Numbers A, Numbers B
WHERE ABS(@n2 - @n1) + 1 < 65537
);
Ответ 5
Это должно быть достаточно быстро, когда вы сначала создаете календарь, а затем используете только соединение. Также для общей цены за гостиницу может быть достигнуто с помощью наборов групп:
Определение данных:
create table HotelRate(HID int, START_DATE date, END_DATE date, PRICE_PER_DAY int);
insert into HotelRate values
(1, '20150101', '20150110', 100),
(1, '20150111', '20150120', 75),
(1, '20150121', '20150130', 110),
(2, '20150101', '20150110', 10),
(2, '20150111', '20150120', 5),
(2, '20150121', '20150130', 50)
Query:
declare @sd date = '20150105' , @ed date = '20150125'
;with c as(select @sd d union all select dateadd(dd, 1, d) from c where d < @ed)
select h.HID, h.START_DATE, h.END_DATE, sum(PRICE_PER_DAY) PRICE
from c join HotelRate h on c.d >= h.START_DATE and c.d < h.END_DATE
group by grouping sets((h.HID, h.START_DATE, h.END_DATE),(h.HID))
Вывод:
HID START_DATE END_DATE PRICE
1 2015-01-01 2015-01-10 500
1 2015-01-11 2015-01-20 675
1 2015-01-21 2015-01-30 550
1 (null) (null) 1725
2 2015-01-01 2015-01-10 50
2 2015-01-11 2015-01-20 45
2 2015-01-21 2015-01-30 250
2 (null) (null) 345
Это может быть дополнительно оптимизировано таблицами таблиц. И даже более того, если вы создадите таблицу календаря в своей базе данных, она будет мгновенной.
Вот скрипка http://sqlfiddle.com/#!3/25e7bc/1
Предположим, что вы создали календарную таблицу Calendar(d date)
, которая содержит даты, начинающиеся с примера 1900-01-01
end 2100-01-01
. Добавьте индексы в таблицы Calendar
и HotelRange
в столбцы даты. Затем над запросом можно переписать как:
select h.HID, h.START_DATE, h.END_DATE, sum(PRICE_PER_DAY) PRICE
from Calendar c join HotelRate h on c.d >= h.START_DATE and c.d < h.END_DATE
where c.d between @sd and @ed
group by grouping sets((h.HID, h.START_DATE, h.END_DATE),(h.HID))