Oracle PL/SQL 触发器以限制每个客户的订单

Posted

技术标签:

【中文标题】Oracle PL/SQL 触发器以限制每个客户的订单【英文标题】:Oracle PL/SQL Trigger to limit orders per customer 【发布时间】:2013-03-21 11:20:42 【问题描述】:

只是在创建一个小触发器。我想要这个触发器做的是确保一个客户在当前时间只能在下订单表中有 10 个订单。不要问我的客户为什么要这个,这对我来说似乎很愚蠢哈。

但基本上触发器是在下订单表上,我目前在 customer_ID 上选择 DISTINCT COUNT 并将其放入 v_count VARIABLE。

IF v_count < 10 INSERT INTO placed_order
ELSE
DBMS_OUTPUT.PUT_LINE ('you have 10 or more orders processing please wait')
END if
END

这是代码的基本要点,但它无法运行如果有人愿意,我可以显示完整的代码吗?

这是代码 - 抱歉,我现在不知道如何使用 SQLFiddle。

     CREATE OR REPLACE TRIGGER trg_order_limit
     BEFORE INSERT
     ON placed_order
     FOR EACH ROW
     DECLARE
     v_count number;
     BEGIN
     SELECT COUNT(DISTINCT FK1_customer_id) FROM placed_order into v_count;
     if v_count < 10 then
     INSERT INTO placed_order
     (order_id,  order_date, delivery_date, FK1_customer_id, FK2_employee_id,        FK3_Order_type_id)
     VALUES
     (:NEW.order_id, :NEW.order_date, :NEW.delivery_date, :NEW.FK1_customer_id,     :NEW.FK2employee_id, :NEW.FK3_order_type_id);
     ELSE
     v_count > 10 then
     DBMS_OUTPUT.PUT_LINE('You currently have 10 or more orders processing.');
     end if;
     end;

当我在 oracle 中运行脚本时,我得到 第 4 行错误:PL/SQL: ORA-00933: SQL 命令未正确结束

非常感谢 理查德

【问题讨论】:

是的,请添加完整代码,如果可能,请创建一个 SQLfiddle 来显示问题。 “不会运行”是什么意思?你有错误吗? 触发器不能安全地用于实现这种逻辑(想想并发插入......触发器错过了它们并允许违反该规则)。你需要在 API 中控制它(序列化它)或者物化视图也可以做到。 您永远不应该将业务逻辑/规则放在数据库中。如果您绝对必须,您应该为此使用 CONSTRAINT,而不是 TRIGGER。 @FrankSchmitt 我想这取决于其他要求。如果您想要一条“所有客户永远不能拥有超过 10 个订单”的一揽子规则,那么当然可以。但也许您想区分“普通”和“高级”客户?很快,您的 CONSTRAINT 变得不平凡且无法维护。此外,您真的应该尝试以这样一种方式设计您的软件,即“最终用户可以不受限制地访问底层数据存储”不是您需要特别考虑的场景。​​ 【参考方案1】:

即使您的触发器没有语法错误,它也不会起作用:它只会输出“不能超过 10 个订单”消息,并且插入仍会继续;此外,如果它通过了测试,您将被放入插入循环中。如果记录太多,您需要让它抛出异常,并且您的应用程序需要捕获它,如果它通过测试则什么也不做。

至于错误,我认为问题出在这一行:

SELECT COUNT(DISTINCT FK1_customer_id) FROM placed_order into v_count;

应该是:

SELECT COUNT(DISTINCT FK1_customer_id) into v_count FROM placed_order;

但是,这个查询无论如何都是错误的:它将返回已下订单的唯一客户的数量。您正在寻找的是:

select count(order_id) into v_count from placed_order where fk1_customer_id = :new.fk1_customer_id

假设order_id具有唯一性约束;这似乎很可能!无论如何,因此,您的触发代码应该是这样的:

create or replace trigger trg_order_limit
before insert on placed_order for each row
declare
  v_count number;
begin
  -- Get current order count
  select count(order_id)
  into   v_count
  from   placed_order
  where  fk1_customer_id = :new.fk1_customer_id;

  -- Raise exception if there are too many
  if v_count >= 10 then
    raise_application_error(-20000, 'You currently have 10 or more orders processing.');
  end if;
end;

但是,根据@DazzaL's comment,这种方法可能不是一个好主意。


编辑 突然意识到您计算每个客户的订单数量的查询是完全错误的。更新了我的答案。

【讨论】:

嗨,非常感谢它对我的帮助很大。我完全同意你的观点,创建这个不是好习惯,我一开始就说过这是客户的要求,也许我会向他展示这个线程并尝试让他明白哈。再次感谢您的帮助和时间 更新了我的答案,因为按客户 ID 计算订单的查询确实是错误的:P 哦,是的,我也注意到了,谢谢您的帮助。当我现在尝试插入应该失败的行时,它会返回一个令人讨厌的 oracle 错误 ORA-20000:您当前有 10 个或更多订单正在处理。 ORA-06512:在“C3283535.TRG_ORDER_LIMIT”,第 12 行 ORA-04088:执行触发器 'C3283535.TRG_ORDER_LIMIT' 时出错 这是否可以通过错误处理进行更改? 就是这个主意!!正如我所说,您的应用程序需要捕获该错误——如果/何时发生——并以某种有意义的方式将其报告给客户端。

以上是关于Oracle PL/SQL 触发器以限制每个客户的订单的主要内容,如果未能解决你的问题,请参考以下文章

PL/SQL Oracle 错误处理

PL/SQL ORACLE:删除时触发更新

Oracle PL / SQL触发器,在UPDATE之前/之后仅用于识别表中已修改的列

Oracle笔记4-pl/sql-分支/循环/游标/异常/存储/调用/触发器

使用 PL\SQL Developer 7.0.2 调试 Oracle 触发器?

Oracle 12 PL/SQL 在触发器中检索存储过程名称