繁体   English   中英

基于变化表中的值在插入之前触发 PL/SQL 触发器

[英]PL/SQL Trigger BEFORE INSERT based on values in the changing table

所以我有这个问题。

如果客户在插入每一行之前已经使用触发器至少有一个未付款的购物车(以防插入多个新购物车),我想防止添加新的购物车。

表的 model 截图

我设法在修改表的paidTime 列中包含 NULL 的 T-SQL 计数记录中很容易地解决了这个问题。

CREATE TRIGGER one_unpaid_cart_per_client  ON Cart
FOR INSERT
AS
BEGIN
    SET NOCOUNT ON;
    DECLARE newCart_cursor CURSOR FOR SELECT IdClient, IdCart, isPaid FROM inserted;

    DECLARE @IdClient int;
    DECLARE @IdCart int;
    DECLARE @isPaid datetime;

    OPEN newCart_cursor;
    FETCH NEXT FROM newCart_cursor INTO @IdClient, @IdCart, @isPaid;

    WHILE @@FETCH_STATUS = 0
    BEGIN
        IF @isPaid is null
        BEGIN
            if 1 < (SELECT COUNT(1) FROM Cart WHERE IdClient = @IdClient AND isPaid is Null)
            BEGIN
                PRINT 'Unpaid cart already exists for client id: ' + Cast(@IdClient as Varchar);
                DELETE FROM Cart WHERE IdCart = @IdCart;
                Raiserror('New cart has not been added', 1, 1);
            END;
        END;
        FETCH NEXT FROM newCart_cursor INTO @IdClient, @IdCart, @isPaid;
    END;

    CLOSE newCart_cursor;
    DEALLOCATE newCart_cursor;
    SET NOCOUNT OFF;
END;

在 PL/SQL 中,据我所知,您无法查询更改表,因此我无法检查那里有多少未付费的购物车。

我可以做些什么来使用 PL/SQL 中的触发器来完成这项工作吗?

谢谢你的帮助!

正如 OldProgrammer 建议的那样,您可以使用 Oracle 中的复合触发器来完成您想做的事情。 在这种方法中,您将有一个 BEFORE INSERT...FOR EACH ROW 触发器来记录 memory(PL/SQL 数组或其他东西)中的客户端 ID,然后是一个 BEFORE INSERT 语句级别触发器来检查多个未付费购物车中的任何一个记录的客户端 ID(如果找到,则抛出异常)。

但是,使用触发器来强制执行参照完整性是很难做到的。 这主要是因为触发器在事务提交之前触发。 因此,例如:

  • Session A,时间 1:为客户 100 插入未付款的购物车(触发检查通过)
  • Session B,时间 2:为客户 100 插入未付款的购物车(触发检查也通过,因为 session A 尚未提交!)
  • Session A:时间 3:提交
  • Session B:时间 4:提交

...而您的触发器未能实现其目的。

一个更好的方法是创建一个唯一约束,依赖于唯一约束不拒绝在约束的所有列中具有NULL值的行这一事实。

像下面的例子应该避免上面描述的竞争条件并且 100% 的时间工作。

--drop table cart;

CREATE TABLE cart
  ( cart_id          NUMBER NOT NULL,
    client_id        NUMBER NOT NULL,
    payment_time     TIMESTAMP,
    unpaid_client_id NUMBER 
      INVISIBLE GENERATED ALWAYS AS (DECODE(payment_time,NULL,client_id, NULL)) VIRTUAL,
    is_unpaid        VARCHAR2(1) 
      INVISIBLE GENERATED ALWAYS AS (DECODE(payment_time,NULL,'Y',NULL)) VIRTUAL,
    CONSTRAINT cart_pk PRIMARY KEY ( cart_id ),
    CONSTRAINT max_one_unpaid UNIQUE ( unpaid_client_id, is_unpaid)
  );

delete from cart;  
  
-- This should be allowed: the first unpaid cart for a client
insert into cart ( cart_id, client_id, payment_time ) values ( 100, 1, null );
--1 row(s) inserted.

-- This should be rejected: a second unpaid cart for a client
insert into cart ( cart_id, client_id, payment_time ) values ( 101, 1, null );
--ORA-00001: unique constraint (SQL_PRUOVRWCMSHZECACDIWPGIBED.MAX_ONE_UNPAID) violated ORA-06512: at "SYS.DBMS_SQL", line 1721

-- This should be allowed, a paid cart for a client having an unpaid one    
insert into cart ( cart_id, client_id, payment_time ) values ( 102, 1, SYSTIMESTAMP );
--1 row(s) inserted.

-- This should be allowed, a second paid cart for a client having an unpaid one    
insert into cart ( cart_id, client_id, payment_time ) values ( 103, 1, SYSTIMESTAMP );
--1 row(s) inserted.

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM