Помогите с запросом SQL найти следующую доступную дату для системы бронирования - PullRequest
2 голосов
/ 26 февраля 2011

Я пытаюсь написать SQL-запрос для системы бронирования, чтобы найти самую раннюю доступную дату проката для элемента.

Элементы классифицированы в базе данных по артикулу.Может быть несколько копий предмета, каждый из которых уникально идентифицирован в базе данных по его серийному номеру.

При поиске самой ранней доступной даты аренды предмета не имеет значения, какой серийный номер выбран;просто следующий доступный.

База данных имеет 2 таблицы;«Бронирование» и «Предметы».Также есть таблица Календаря с несколькими тысячами будущих дат ГГГГ-ММ-ДД для работы.

Таблица Резервирования содержит столбцы;«record_number», «sku», «serial_number», «start_date», «end_date» плюс данные клиента.Здесь каждое резервирование записывается в том виде, в котором оно было сделано.

Таблица Items содержит столбцы;"sku" и "serial_number".Это список всех предметов аренды в системе.

Я работал над этой проблемой более 2 дней, но моих знаний по SQL недостаточно для решения этой головоломки.

I 'мы продвинулись настолько далеко, что сгенерировали список дат, у которых есть хотя бы одно резервирование для конкретного SKU:

   SELECT calendar.dt
     FROM calendar
LEFT JOIN reservations ON calendar.dt >= reservations.start_date 
                      AND calendar.dt <= reservations.end_date
    WHERE reservations.sku = 'ABC123'

Я могу выполнить подзапрос вышеизложенного в операторе выбора «NOT IN ...», но этовыполняет только поиск дат без резервирования для конкретного артикула.Мне нужно найти первую дату, когда хотя бы один элемент доступен.

Я представлял себе, как соединить даты таблицы Календаря с SKU из таблицы Предметов с номерами резервирования таблицы Резервирования, ища "NULL"."в booking_record, указывая, что резервирование для этой даты и комбинации серийного номера не существует.Но я не смог написать такой запрос, который работает.

Вопросы приветствуются.

Ответы [ 4 ]

2 голосов
/ 26 февраля 2011

Следующее должно помочь вам.Вы можете настроить мой пример функции «Current_Date ()» для любой даты начала резервирования и выходов на такое количество дней ....

При этом используются встроенные переменные MySQL в запросе.Внутренний запрос представляет собой простую подготовку переменной резервирования (@r), основанную на некоторой начальной дате (current_date ()), и присоединяется к таблице элементов.Если вы не сделаете предложение о присоединении, в противном случае он получит одну дату для каждого элемента.В моем сценарии я рассматриваю только выход на 30 дней, поэтому я применил ограничение на первые 30 пунктов.Никаких оснований, кроме как дать мне достаточно записей, поэтому мне не нужно создавать временную таблицу из 30 записей (или сколько бы дней вы ни захотели выйти).Это создает псевдоним запроса «JustDates» и имеет один столбец «OpenDate».Это основа диапазонов дат для проверки.

Теперь она присоединена к таблице элементов, но никакое условие не создает декартово выражение для каждой даты, сравнивают с каждым элементом ... согласно предложению WHERE,Я имею дело только с предметами, имеющими SKU «ABC123», если у них есть 10 серийных номеров или 100. Теперь это даст мне 300 или 3000 (10 серийных изделий при 30 днях или 100 серийных изделий при 30 днях.

Теперь, когда у меня есть «диапазон» всех отдельных серийных номеров и возможных дней, чтобы проверить доступность, я могу теперь запрашивать данные в системе резервирования. Таким образом, с помощью дополнительного выбора, а НЕ IN для данного соответствующего SKU,СЕРИЙНЫЙ # и ВОЗМОЖНАЯ Дата, найденная в резервациях, я хочу сохранить только те, в которых данный OpenDate НЕ найден. Я смоделировал ваши структуры таблиц и добавил несколько элементов, несколько серийных номеров и диапазоны дат резервирования иэто прекрасно работает ...

Очевидно, я бы обеспечил индексы на sku / serial для производительности. Единственное дополнительноея могу внести изменения при запросе к резервированию, чтобы исключить любые резервирования, если конечная дата предшествует дате начала, о которой идет речь, для ВАШЕГО запроса, и, при желании, нет даты начала> последней даты, которую вы рассматриваете.Если у вас есть тонна бронирований, охватывающая годы, кому небезразлично что-то древнее или что-то еще в будущем из рассматриваемого диапазона дат.

select  items.sku,
        items.serial_number,
        JustDates.OpenDate
    from 
        ( SELECT 
                 @r:= date_add(@r, interval 1 day ) OpenDate
            FROM 
                 (select @r := current_date()) vars,
                items limit 30 ) JustDates,
        items
    where 
            sku = "ABC123"
        and sku not in ( select sku from Reservations
                            where items.sku = reservations.sku
                              and items.serial_number = reservations.serial_number
                              and justDates.OpenDate >= reservations.start_date
                              and justDates.OpenDate <= reservations.end_date )
    order by 
       items.serial_number,
       justDates.OpenDate
1 голос
/ 26 февраля 2011

К сожалению, вы не можете искать пропущенные строки (вы можете найти строки, которые не существуют, но это не одно и то же).

Вы хорошо начали, запросив таблицу календаря и оставив присоединение к резервированию, но допустили ошибку, поместив условие фильтрации в предложение WHERE, где оно не будет возвращать никаких строк вместо предложения join ON.

SELECT calendar.dt,
   COUNT(DISTINCT reservations.id) AS reserved,
   COUNT(DISTINCT items.id) AS inventory
FROM calendar
LEFT JOIN reservations
   ON calendar.dt >= reservations.start_date 
   AND calendar.dt <= reservations.end_date
   AND reservations.sku = 'ABC123'
LEFT JOIN items ON items.sku=reservations.sku
GROUP BY calendar.dt
HAVING inventory > reserved OR reserved = 0
ORDER BY calendar.dt ASC 
LIMIT 1

В этом запросе выполняется поиск самой ранней даты в таблице календаря, где существующее количество элементов в таблице элементов превышает количество резервирований на эти даты (т. Е. Первая дата, когда у вас есть запас).

( РЕДАКТИРОВАТЬ : заменить 'id' первичным столбцом в этих таблицах)

1 голос
/ 26 февраля 2011

Я получил очень ценный урок от эксперта по SQL очень давно.

Не позволяйте тому, что у вас есть, ослепить вас к тому, что вам нужно . Подумайте, где в реальном мире используются бронирования.

  • отели
  • Авиакомпании
  • Круизные лайнеры
  • Аренас
  • Футбольные стадионы

Каждый из них имеет фиксированный перечень мест в определенный день. Каждое из этих мест может быть либо открытым, либо зарезервированным - привязанным к конкретному клиенту.

У вас есть фиксированный инвентарь предметов в определенный день. Каждый из этих предметов может быть доступен или зарезервирован - привязан к конкретному клиенту.

Я думаю, что ваша жизнь была бы намного проще, если бы вы создали таблицу доступности, а не таблицу бронирования.

create table availability (
  sku varchar(15) not null,
  sn varchar(15) not null,
  av_date date not null,
  customer_id integer, --references customers (customer_id)
  primary key (sku, sn, av_date),
  foreign key (sku, sn) references items (sku, sn)
);

insert into availability values
('1', '1', '2011-01-01', 1),  -- reserved by customer_id 1
('1', '1', '2011-01-02', 1),
('1', '1', '2011-01-03', 1),
('1', '1', '2011-01-04', NULL), -- not yet reserved
('1', '1', '2011-01-05', NULL),
('1', '1', '2011-01-06', NULL),
('1', '1', '2011-01-07', NULL),
('1', '1', '2011-01-08', NULL),
('1', '1', '2011-01-09', NULL),
('1', '1', '2011-01-10', NULL),
('1', '2', '2011-01-01', NULL),
('1', '2', '2011-01-02', 2),
('1', '2', '2011-01-03', 2),
('1', '2', '2011-01-04', 3),
('1', '2', '2011-01-05', 3),
('1', '2', '2011-01-06', NULL),
('1', '2', '2011-01-07', NULL),
('1', '2', '2011-01-08', NULL),
('1', '2', '2011-01-09', NULL),
('1', '2', '2011-01-10', NULL);

(Для производства я бы собрал хранимую процедуру для заполнения таблицы доступности.)

Теперь просто выбрать самую раннюю доступную дату для данного sku.

select sku, sn, av_date, customer_id 
from availability 
where sku = '1' and customer_id is null
order by av_date asc
limit 1
0 голосов
/ 26 февраля 2011

Даты начала и окончания запроса - это периоды времени, в течение которых кто-то хочет зарезервировать данный предмет. (Например, «Я хочу зарезервировать виджет где-то между июлем и сентябрем»)

Пересмотрен для обработки имеющегося запаса в наличии. Хотя не совсем понятно, как это хранится. У вас есть в наличии значение для каждого SKU или каждому элементу присвоен свой ряд?

Select Min( C.dt ) As EarliestAvailableDate
From Calendar As C
    Left Join Reservations As R
        On R.StartDate <= C.dt
            And R.EndDate >= C.dt
            And R.Sku = 'ABC123'
Where C.dt Between '[Request Start Date]' And '[Request End Date]'
    And R.record_number Is Null
    And Exists  (
                Select 1
                From Items As I1
                    Left Join Reservations As R1
                        On R1.StartDate <= C.dt
                            And R1.EndDate >= C.dt
                            And R1.Sku = I1.SKU
                Where I.SKU = 'ABC123'
                Having Count(*) > Count(R1.record_number)
                )

Если в вашей таблице товаров на самом деле хранится имеющееся в наличии значение для номера данного SKU на складе, тогда вы можете просто изменить запрос следующим образом:

Select Min( C.dt ) As EarliestAvailableDate
From Calendar As C
    Left Join Reservations As R
        On R.StartDate <= C.dt
            And R.EndDate >= C.dt
            And R.Sku = 'ABC123'
Where C.dt Between '[Request Start Date]' And '[Request End Date]'
    And R.record_number Is Null
    And Exists  (
                Select 1
                From Items As I1
                    Left Join Reservations As R1
                        On R1.StartDate <= C.dt
                            And R1.EndDate >= C.dt
                            And R1.Sku = I1.SKU
                Where I.SKU = 'ABC123'
                Having I1.OnHandCount > Count(R1.record_number)
                )
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...