Oracle Trigger для удаления количества товара из продуктов и добавления в строку заказа - PullRequest
2 голосов
/ 06 декабря 2011

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

Теперь объект заказа имеет отношение один-ко-многим к order_lineсубъект, который, в свою очередь, имеет отношение один к одному с продуктом (который сам хранится на складе и т. д.).Сущность order_line является сущностью связи и решает отношение «многие ко многим», так что это все хорошо.Просто чтобы уточнить, это одна строка заказа на продукт.

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

В случае успеха я хочу соответствующим образом обновить атрибуты количества и запаса.Я также хотел бы добавить промежуточную сумму к order_line (я еще не пытался это сделать), которая, в свою очередь, может затем использоваться для вычисления общего значения в сущности order.

Итак, на этом этапеЯ ищу немного руководства, потому что сейчас я очень смущен этим.

CREATE OR REPLACE TRIGGER check_order_line  
BEFORE INSERT OR UPDATE ON order_line 

for each row
BEGIN 
select order_line.quantity, products.stock from order_lines right join products on       order_line.product_no=products.product_no;
if(order_line.quantity>products.stock) then
RAISE_APPLICATION_ERROR(-20103, 'Insufficient Stock');
ELSE
products.stock := products.stock - quantity;
 dbms_output.put('Successful');
END IF;
END; 
. 
run

Ошибки, которые я получаю:

2/1      PL/SQL: SQL Statement ignored
2/49     PL/SQL: ORA-00942: table or view does not exist
3/1      PL/SQL: Statement ignored
3/15     PLS-00357: Table,View Or Sequence reference 'ORDER_LINE.QUANTITY'
     not allowed in this context

Что я пробовал:

  • Я не уверен насчет первых двух ошибок;рассматриваемая таблица определенно называется order_line, возможно, я пропустил что-то очевидное.
  • Я также попытался объявить переменные для products.stock и order_line кол-ва, чтобы решить последнюю ошибку - это компилирует IIRC, но на самом деле не работает, так как, я думаю, это не обновляет таблицу.
  • Меня не очень беспокоит действие else, возможно, мне нужен там оператор обновления таблицы, но сейчас я просто сосредоточен на том, чтобы заставить работать условие триггера.
  • Если кто-нибудь может указать мне правильное направление и указать на любые смешные ошибки, я буду признателен за это.

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

Ответы [ 2 ]

3 голосов
/ 06 декабря 2011

Установите ограничение на products.stock, чтобы принудительно установить значение> -1:

ALTER TABLE products add CONSTRAINT has_stock CHECK (stock >-1);

Затем выполните update и insert как одну транзакцию.

UPDATE product SET products.stock = products.stock - quantity_required
WHERE product_id=id_of_product

INSERT INTO order_line ...............

COMMIT;

Транзакция всегда будет неудачной, если на складе недостаточно, и у вас не будет проблем, связанных с триггерами.

Предполагая, что на данный момент у вас нет цены на акции, вы можете получить ее, используя предложение RETURNING на вашем update (вам нужно ОБЪЯВИТЬ переменную v_product_cost для хранения значения), например:

UPDATE product SET products.stock = products.stock - quantity_required
WHERE product_id=id_of_product
RETURNING products.value INTO v_product_cost

Затем вы можете использовать это значение в следующей вставке.

1 голос
/ 06 декабря 2011
  1. Команда run в конце не имеет смысла. Это синтаксис SQL Server.
  2. В вашем запросе вы ссылаетесь на таблицу ORDER_LINES (множественное число). Но триггер определен для таблицы ORDER_LINE (единственное число). Я предполагаю, что у вас нет таблиц ORDER_LINE и ORDER_LINES, поэтому я предполагаю, что вы хотели, чтобы ваш запрос ссылался на таблицу ORDER_LINE.
  3. Триггер уровня строки, определенный в таблице A, не может в общей таблице запросов A. Поэтому, поскольку ваш триггер определен для ORDER_LINE, он не может запрашивать ORDER_LINE. Похоже, что вы просто хотите получить информацию о строке, которая вызвала срабатывание триггера, поэтому вам на самом деле не нужно присоединяться к таблице ORDER_LINE. Вам просто нужно сослаться на атрибуты из записи :NEW.
  4. Оператор SELECT в PL / SQL должен что-то делать с результатами. Предположительно, вы намерены сделать SELECT ... INTO локальную переменную.
  5. Если вы хотите обновить таблицу PRODUCTS, вам нужно будет сделать UPDATE.

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

CREATE OR REPLACE TRIGGER check_order_line  
  BEFORE INSERT OR UPDATE ON order_line 
  for each row
DECLARE
  l_current_stock products.stock%type;
BEGIN 
  select products.stock 
    into l_current_stock
    from products
   where product_no = :new.product_no;
  if(:new.quantity > l_current_stock) then
    RAISE_APPLICATION_ERROR(-20103, 'Insufficient Stock');
  else
    update products
       set stock := stock - :new.quantity
     where product_no := :new.product_no;
  end if;
END;

Однако, как говорится, триггер, как правило, не является правильным способом решения такого рода проблемы. С точки зрения обслуживания, если ничего больше, с хранимой процедурой PROCESS_ORDER, которая вставляет все строки ORDER_LINE и обновляет все строки PRODUCTS, будет намного проще следить и отлаживать. Чем больше бизнес-логики внедрено в триггеры, тем сложнее следить за ходом приложения и тем легче в конечном итоге оказаться в гнезде непреднамеренных обновлений, которые почти невозможно раскрутить.

Кроме того, помните о том, что происходит в многопользовательской системе. Сессия A может запросить таблицу PRODUCTS и увидеть STOCK из 5 и принять заказ на 4 единицы этого продукта. Но до того, как сессия A выдает коммит, сессия B также запрашивает ту же строку таблицы PRODUCTS, видит тот же STOCK из 5 и принимает заказ на 3 единицы. Оператор UPDATE сеанса B будет блокироваться, пока сеанс A не завершится. Но затем, если A фиксирует и B фиксирует, оба ордера будут введены, и в таблице PRODUCTS будет отображаться STOCK -2. Вот почему вам нужно ограничение CHECK, которое Кевин предложил

ALTER TABLE products
  ADD CONSTRAINT chk_positive_stock CHECK( stock >= 0 );
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...