У меня есть таблица с резервированием. Каждая строка является резервированием и имеет поле start
& end
datetime.
Я хочу построить запрос, который дает мне количество оговорок на каждый день за определенный промежуток времени, например, апрель 2018 года.
Выбор всех оговорок за данный интервал довольно прост:
SELECT * FROM reservation
WHERE start <= '2018-05-01 00:00:00'
AND end >= '2018-04-01 00:00:00'
Но тогда начинается "проблема".
Я хочу отображать "подсчет" оговорок на каждый день в интервале. Но бронирование может длиться несколько дней. Так что группировка их в DAY(start)
неверна.
Я не хочу запрашивать каждый день в промежутке отдельно, так как это будет очень интенсивно работать на сервере.
Есть ли способ сделать это через MySQL-запрос?
Пример данных:
id | start | end
2 | 2018-04-01 12:00:00 | 2018-04-03 09:00:00
3 | 2018-04-01 09:00:00 | 2018-04-01 11:00:00
4 | 2018-04-06 13:00:00 | 2018-05-20 09:00:00
Результат за 2018-04-01
по 2018-04-06
:
2018-04-01 | 2 (2/3)
2018-04-02 | 1 (2)
2018-04-03 | 1 (2)
2018-04-04 | 0
2018-04-05 | 0
2018-04-06 | 1 (4)
в sqlfiddle: http://sqlfiddle.com/#!9/e62ffa/2/0
Сначала мы повторно используем ответ из DBA StackExchange. (Вы можете использовать принятый ответ, если хотите, вам просто нужно создать для него выделенную таблицу).
Мы немного изменим запрос, используя условие, которое вам нужно.
Ваше состояние:
SELECT * FROM reservation WHERE start <= '2018-05-01 00:00:00' AND end >= '2018-04-01 00:00:00'
Измененный ответ от DBA Stackexchange:
SELECT date_field
FROM
(
SELECT
MAKEDATE(YEAR(NOW()),1) +
INTERVAL (MONTH(NOW())-1) MONTH +
INTERVAL daynum DAY date_field
FROM
(
SELECT t * 10 + u daynum
FROM
(SELECT 0 t UNION SELECT 1 UNION SELECT 2 UNION SELECT 3) A,
(SELECT 0 u UNION SELECT 1 UNION SELECT 2 UNION SELECT 3
UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7
UNION SELECT 8 UNION SELECT 9) B
ORDER BY daynum
) AA
) AAA
/*WHERE MONTH(date_field) = MONTH(NOW())*/
WHERE date_field BETWEEN '2018-04-01' AND '2018-05-01'
Обратите внимание, что я только изменил WHERE
.
Теперь, используя этот запрос в качестве DERIVED TABLE
, мы будем включать вашу таблицу Reservations
используя LEFT JOIN
.
SELECT D.date_field
, COUNT(R.Id)
FROM (
/* The query from above goes here */
) D
LEFT JOIN Reservations R ON D.date_field BETWEEN DATE(R.StartDate) AND DATE(R.EndDate)
GROUP BY D.date_field
Заметим еще раз, что мы использовали функцию DATE
чтобы усечь часть TIME
нашего StartDate
и EndDate
потому что, например, для некоторых под капотом я не совсем знаком.2018-04-01
обозначает весь день, и он не может быть между 2018-04-01 09:00:00
и 2018-04-01 11:00:00
Вот демоверсия SQL Fiddle для результата.
Если кто-то может помочь мне в этом. SELECT '2018-04-02' BETWEEN '2018-04-01 23:59:59' AND '2018-04-02 00:00:00'
приведет к 1
(TRUE
). Кажется, что по умолчанию DATE
будет иметь TIMESTAMP
00:00:00
.
В приведенном выше запросе из DBA StackExchange перечислены только дни текущего месяца. Я попытался немного поискать и нашел этот qaru.site/questions/8819/.... Вот часть запроса:
SELECT CURDATE() - INTERVAL (A.A+ (10 * B.A)) DAY AS Date
FROM (
SELECT 0 AS A UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4
UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9
) AS A
CROSS JOIN (
SELECT 0 AS A UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4
UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9
) AS B
Вышеуказанный запрос будет генерировать числа (от 1 до 100), используя CROSS JOIN
а затем вычитать его на Current Date
, после чего у вас будут даты отныне до 100 дней назад. Вы можете добавить еще один CROSS JOIN
номеров для генерации 1000 номеров, если это необходимо.
Я предполагаю, что у вас будут StartDate
и EndDate
в вашей хранимой процедуре или где-нибудь. Мы можем заменить CURDATE
на EndDate
а затем у нас будет 100 дней назад к нашему EndDate
. Мы просто добавим предложение WHERE
, чтобы отфильтровать только даты, которые нам нужны, с использованием подзапроса/производной таблицы.
SELECT D.Date
FROM (
SELECT CURDATE() - INTERVAL (A.A+ (10 * B.A)) DAY AS Date
FROM (
SELECT 0 AS A UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4
UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9
) AS A
CROSS JOIN (
SELECT 0 AS A UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4
UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9
) AS B
) AS D
WHERE D.Date BETWEEN @startDate AND @endDate
Теперь мы можем использовать LEFT JOIN
для включения таблицы Reservations
.
Вот еще одна демонстрация SQL Fiddle для этого. Сюда также входят переменные "Начальная и конечная дата" и примерный диапазон дат, начиная с предыдущего года и текущего года.
Опять же, если вам нужно более 100 дней диапазона, нам просто нужно добавить еще один CROSS JOIN
номеров, пусть назовите это как C
:
CROSS JOIN (
SELECT 0 AS A UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4
UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9
) AS C
А затем добавьте его в расчет прошлых дней в SELECT
.
SELECT CURDATE() - INTERVAL (A.A + (10 * B.A) + (100 * C.A)) DAY AS Date
WHERE date_field BETWEEN '2018-04-01' AND '2018-05-01'
не принимает от даты во внимание.