Подсчет резервирований для каждого дня, где res может занимать несколько дней и должен рассчитываться для каждого дня.

0

У меня есть таблица с резервированием. Каждая строка является резервированием и имеет поле 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

  • 0
    Просто чтобы уточнить, если резервирование охватывает несколько дней (например, с 1 по 5 день), то в результате мы будем иметь счет 1 (как минимум) с 1 по 5 день.
  • 1
    Jups. Я добавлю некоторые образцы данных.
Показать ещё 3 комментария
Теги:

1 ответ

2
Лучший ответ

Сначала мы повторно используем ответ из 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.

Обновление для более гибкого диапазона дат (2018-04-11)

В приведенном выше запросе из 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
  • 0
    В вашем ответе есть небольшая ошибка. Если я изменю время начала на то, что раньше (скажем, 1-е декабря 2017 г.), запрос начнется только с первого апреля 2018. Поэтому поле WHERE date_field BETWEEN '2018-04-01' AND '2018-05-01' не принимает от даты во внимание.
  • 0
    @stUrb Я обновил свой ответ :) Если это поможет вам, я думаю, вы можете снова проголосовать.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню