Триггер, чтобы разрешить 1 автомобиль для каждого клиента и запретить другому клиенту арендовать уже арендованный автомобиль в период между арендой и датой возврата.

У меня есть проект на основе баз данных (Oracle) по созданию магазина проката автомобилей. До сих пор все шло хорошо, но я застрял на триггере, который должен проверять клиента, а также автомобиль между арендой и датой возврата, так что клиент, который уже арендовал автомобиль, не может арендовать другой, а другой клиент не может арендовать уже арендованный автомобиль.

Аренда стола:

CREATE TABLE RentAuto
(
auto_id INT,
customer_id INT,
employee_id INT,
date_rent DATE,
date_return DATE,
rent_days VARCHAR2(5)
);

Таблица автомобилей:

CREATE TABLE Automobile 
(
auto_id INTEGER NOT NULL,
year_of_production VARCHAR(10),
auto_current_km VARCHAR(20),
price_per_day VARCHAR(20),
color_id INT,
is_auto_av INT,
model_id INT,
PRIMARY KEY(auto_id)
);

Внесите в таблицу:

INSERT INTO RentAuto (auto_id,customer_id,employee_id,date_rent,date_return, rent_days)
VALUES(14,13,3,TO_DATE(sysdate, 'dd-mm-yyyy'), '29-03-2022','2');

Триггер, который я написал ниже, выдает ошибку в операторе IF, и я не могу понять, как ее исправить. (PLS-00201: должен быть объявлен идентификатор NEW.CUSTOMER_ID.)

create or replace TRIGGER RENTINGTRIGGER
BEFORE INSERT OR UPDATE OF AUTO_ID,CUSTOMER_ID,DATE_RENT,DATE_RETURN ON RENTAUTO
FOR EACH ROW
BEGIN
IF RENTAUTO.CUSTOMER_ID = :NEW.CUSTOMER_ID
and ((new.DATE_RENT >= RentAuto.DATE_RETURN and new.DATE_RENT < RentAuto.DATE_RETURN)
        or (new.DATE_RETURN > RentAuto.DATE_RENT and new.DATE_RETURN < RentAuto.DATE_RETURN))
THEN
RAISE_APPLICATION_ERROR(-2099, 'You can only book one car per single customer a day');
IF RentAuto.AUTO_ID = :NEW.AUTO_ID
and 
 ((new.date_rent >= RentAuto.date_return and new.date_rent < RentAuto.date_return)
    or (new.date_return > RentAuto.date_rent and new.date_return < RentAuto.date_return))
THEN
RAISE_APPLICATION_ERROR(-2099, 'Car has already been rented by another customer!');
END IF;
END IF;
END;

Почему вы используете триггер для реализации этого вместо контрольного ограничения? Также может быть возможно реализовать использование только уникального ограничения.

Dai 09.04.2022 17:06

@Dai Я знаю, что реализовывать это как триггер - плохая практика, но я должен сделать это с помощью триггера (так сказал человек, который передал нам проект)

sens31 09.04.2022 17:07

В некоторых местах вам не хватает двоеточия перед new. А в IF RENTAUTO.CUSTOMER_ID какой ряд должен быть RENTAUTO? В триггере есть строка :new и строка :old, вот и все.

Thorsten Kettner 09.04.2022 17:14

@Thorsten Kettner Ну, в основном это попытка сравнить клиента, чей идентификатор находится в RentAuto (таблица), и того, который был недавно вставлен. Я просто не знаю, как это сделать.

sens31 09.04.2022 17:19

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

Thorsten Kettner 09.04.2022 17:19

@ThorstenKettner Да, мне никогда раньше не приходилось реализовывать составной триггер, но я проверю его, большое спасибо!

sens31 09.04.2022 17:22

У вас есть несколько столбцов, которые определены как VARCHAR. В оракуле они должны быть VARCHAR2. Также у вас есть несколько столбцов, которые явно являются числовыми данными, но вы определили их как VARCHAR — year_of_production, auto_current_km, price_per_day, rent_days. И наоборот, у вас есть некоторые столбцы, которые определены как INT, но характер данных не является числовым, даже если по стандарту действительные данные содержат только числовые СИМВОЛЫ — customer_id, employee_id. Эмпирическое правило - если вы не будете заниматься математикой, это не число. Нет необходимости в арендных_днях, так как их всегда можно вычислить из даты_возврата — даты_ренты.

EdStevens 09.04.2022 18:18

@EdStevens Я все изменил, как вы упомянули, большое спасибо! Теперь он полностью работает с новыми значениями столбца, я также установил контрольное ограничение для RentAuto, чтобы дата возврата не могла быть ниже даты аренды: ALTER TABLE RentAuto ADD CONSTRAINT CHECK_PERIOD CHECK (date_rent IS NULL OR date_return > date_rent);

sens31 10.04.2022 02:05
Формы c голосовым вводом в React с помощью Speechly
Формы c голосовым вводом в React с помощью Speechly
Пытались ли вы когда-нибудь заполнить веб-форму в области электронной коммерции, которая требует много кликов и выбора? Вас попросят заполнить дату,...
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Будучи разработчиком веб-приложений, легко впасть в заблуждение, считая, что приложение без JavaScript не имеет права на жизнь. Нам становится удобно...
Flatpickr: простой модуль календаря для вашего приложения на React
Flatpickr: простой модуль календаря для вашего приложения на React
Если вы ищете пакет для быстрой интеграции календаря с выбором даты в ваше приложения, то библиотека Flatpickr отлично справится с этой задачей....
В чем разница между Promise и Observable?
В чем разница между Promise и Observable?
Разберитесь в этом вопросе, и вы значительно повысите уровень своей компетенции.
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Клиент для URL-адресов, cURL, позволяет взаимодействовать с множеством различных серверов по множеству различных протоколов с синтаксисом URL.
Четыре эффективных способа центрирования блочных элементов в CSS
Четыре эффективных способа центрирования блочных элементов в CSS
У каждого из нас бывали случаи, когда нам нужно отцентрировать блочный элемент, но мы не знаем, как это сделать. Даже если мы реализуем какой-то...
0
8
44
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

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

Если оператор вставки или обновления находится более чем в одной строке, триггер не сообщает нам, какое резервирование привело к сбою. Кроме того, каждый раз, когда происходит вставка или обновление, мы должны сканировать всю таблицу на наличие конфликтов. Если вы хотите реагировать только на вставляемые или обновляемые строки, вам нужен составной триггер, в котором вы запоминаете рассматриваемые строки в части «после каждой строки», а затем сравниваете их с другими строками в таблице в часть «после утверждения».

CREATE OR REPLACE TRIGGER rentingtrigger
AFTER INSERT OR UPDATE OF auto_id, customer_id, date_rent, date_return ON rentauto
  v_count INTEGER;
BEGIN
  select count(*)
  into v_count
  from rentauto
  where exists
  (
    select null
    from rentauto other
    where other.customer_id = rentauto.customer_id
    and other.date_rent <= rentauto.date_return
    and other.date_return >= rentauto.date_rent
    and other.rowid <> rentauto.rowid
  );

  IF v_count > 0 THEN
    RAISE_APPLICATION_ERROR(-20099, 'You can only book one car per single customer a day');
  END IF;

  select count(*)
  into v_count
  from rentauto
  where exists
  (
    select null
    from rentauto other
    where other.auto_id = rentauto.auto_id
    and other.date_rent <= rentauto.date_return
    and other.date_return >= rentauto.date_rent
    and other.rowid <> rentauto.rowid
  );

  IF v_count > 0 THEN
    RAISE_APPLICATION_ERROR(-20099, 'Car has already been rented by another customer!');
  END IF;
END rentingtrigger;

Другие вопросы по теме