【问题标题】:Trigger's with identation cursor[Edit]带有标识光标的触发器[编辑]
【发布时间】:2016-06-07 10:42:13
【问题描述】:

各位早上好,

这是我在这里的第一篇文章,我一直在为我的工作开发一个 APP 的数据库,我需要一些关于触发器的线索,这让我很生气。

编辑:

给定下一个结构:

用户表:

[dbo].[ENGIE_Users]
(
[dbID] [int] IDENTITY(1,1) NOT NULL,
[Nombre] [nvarchar](100) NOT NULL,
[Apellidos] [nvarchar](200) NOT NULL,
[HashContraseña] [binary](64) NOT NULL,
[Email] [nvarchar](150) NOT NULL,
[Direccion] [nvarchar](150) NOT NULL,
[Codigo Postal] [nvarchar](5) NOT NULL,
[Telefono] [nvarchar](12) NOT NULL,
[Venta a Nº Cliente] [nvarchar](10) NOT NULL,
[POS Asignado] [nvarchar](10) NOT NULL,
[User_Number]  AS ([dbo].[EngieUsers]([dbID])),
[Salt] [uniqueidentifier] NULL

约束: dbID 主键 电子邮件 No-Pk 唯一

这里是我们从 APP 存储用户的地方。

用户材料表:

[dbo].[ENGIE_User_Material]
(
[ID User] [int] NOT NULL,
[EAN] [nvarchar](13) NOT NULL,
[Product Number] [nvarchar](20) NOT NULL,
[Venta] [tinyint] NOT NULL,
[Cantidad Actual] [int] NOT NULL,
[Cantidad Total] [int] NOT NULL,
[Descripcion Corta] [nvarchar](100) NOT NULL,
[PVP Neto] [decimal](38, 20) NOT NULL,
[% DTO] [decimal](38, 20) NOT NULL,

主键 ID 用户 + 产品编号 + EAN ID 用户引用来自用户的 dbID。

此表用于存储每个用户分配给其“帐户”的产品数量。

订单表:

[dbo].[ENGIE_Ended_Orders]
(
[dbID] [int] IDENTITY(1,1) NOT NULL,
[ID User] [int] NOT NULL,
[Procesado] [tinyint] NOT NULL,
[Finalizado] [tinyint] NOT NULL,
[Fecha de realizacion] [date] NOT NULL,
[OT] [nvarchar](20) NOT NULL,
[Order Id]  AS ([dbo].[EngiePedidos]([dbID])),
[Total Pedido] [decimal](38, 20) NULL,

主键 dbID

ID 用户外键引用来自用户的 dbId 此表将包含订单的“头部”。

订单内容表

[dbo].[ENGIE_Order_Content]    
(
[Order Id] [int] NOT NULL,
[Product Number] [nchar](20) NOT NULL,
[Procesado] [tinyint] NOT NULL,
[Finalizado] [tinyint] NOT NULL,
[Fecha de realizacion] [date] NOT NULL,
[Cantidad] [int] NOT NULL,
[ID Pedido] [nchar](8) NULL,

主键 Product Number + Order Id 外键 Order Id 引用 Orders dbID

因此,鉴于这种结构,我需要的是下一个限制:

在用户材料上: 它自己的实际数量永远不必超过总量。 (插入/更新)

按订单内容: 插入或更新订单时,必须满足上述限制(因此,如果您要求 5,则必须有 5 件该产品可供您购买) 此外,当它被插入/更新/删除时,触发器必须按照公式计算“订单总计”,即(数量 *(价格 - (价格 * 100/折扣)))。

按订单: 所以还有 2 个表(与 Orders/Order-Content 相同的结构,称为 Ended Orders/Ended Order Content),当布尔值 [Finalizado] = 1 时,该表会将这些表的所有行转移到另外 2 个表。并且将恢复与此已结束订单中的那些项目的用户相关联的实际数量。

工作完成:

触发器: 在用户材料上

ALTER TRIGGER [dbo].[Qty_Restriction]
ON [ENGIE].[dbo].[ENGIE_User_Material]
INSTEAD OF INSERT
AS
BEGIN
    SET NOCOUNT ON; 
    IF(SELECT [Cantidad Actual] FROM INSERTED) > (SELECT [Cantidad Total] FROM INSERTED)
    BEGIN
        RAISERROR('La Cantidad Actual no debe superar la Cantidad Total',18,1);
    END 
    ELSE BEGIN
        INSERT INTO ENGIE.dbo.ENGIE_User_Material SELECT [ID User],[EAN],    [Product Number],[Venta],[Cantidad Actual],[Cantidad Total],[Descripcion Corta],    [PVP Neto],[% DTO] FROM INSERTED;
    END
END

这不会让您插入实际数量多于总数量的 reg。

ALTER TRIGGER [dbo].[Qty_Restriction_2]

ON [ENGIE].[dbo].[ENGIE_User_Material]

INSTEAD OF UPDATE

AS

DECLARE @material varchar(13)
DECLARE @pno varchar(20)
DECLARE @usuario int

SET @material = (SELECT [EAN] FROM INSERTED)
SET @pno = (SELECT [Product Number] FROM INSERTED)
SET @usuario = (SELECT [ID User] FROM INSERTED)  

BEGIN
    SET NOCOUNT ON; 
    IF UPDATE([Cantidad Actual])
    BEGIN
        IF(SELECT [Cantidad Actual] FROM INSERTED) > (SELECT [Cantidad Total] FROM INSERTED)
        BEGIN
            RAISERROR('La Cantidad Actual no debe superar la Cantidad Total',18,1);
        END 
        ELSE BEGIN
            UPDATE [ENGIE].[dbo].[ENGIE_User_Material] SET [Cantidad Actual] = (SELECT [Cantidad Actual] FROM INSERTED) WHERE [Product Number] = @pno AND [EAN] = @material AND [ID User] = @usuario  
        END
    END
    IF UPDATE([Cantidad Total])
    BEGIN
        IF(SELECT [Cantidad Actual] FROM INSERTED) > (SELECT [Cantidad Total] FROM INSERTED)
        BEGIN
            RAISERROR('La Cantidad Actual no debe superar la Cantidad Total',18,1);
        END 
        ELSE BEGIN
            UPDATE [ENGIE].[dbo].[ENGIE_User_Material] SET [Cantidad Total] = (SELECT [Cantidad Total] FROM INSERTED) WHERE [Product Number] = @pno AND [EAN] = @material AND [ID User] = @usuario  
        END
    END
    IF UPDATE([Venta])
    BEGIN
        UPDATE [ENGIE].[dbo].[ENGIE_User_Material] SET [Venta] = (SELECT [Venta] FROM INSERTED) WHERE [Product Number] = @pno AND [EAN] = @material AND [ID User] = @usuario  
    END
    IF UPDATE([Descripcion Corta])
    BEGIN
        UPDATE [ENGIE].[dbo].[ENGIE_User_Material] SET [Descripcion Corta] = (SELECT [Descripcion Corta] FROM INSERTED) WHERE [Product Number] = @pno AND [EAN] = @material AND [ID User] = @usuario  
    END
    IF UPDATE([PVP Neto])
    BEGIN
        UPDATE [ENGIE].[dbo].[ENGIE_User_Material] SET [PVP Neto] = (SELECT [PVP Neto] FROM INSERTED) WHERE [Product Number] = @pno AND [EAN] = @material AND [ID User] = @usuario  
    END
    IF UPDATE([% DTO])
    BEGIN
        UPDATE [ENGIE].[dbo].[ENGIE_User_Material] SET [% DTO] = (SELECT [% DTO] FROM INSERTED) WHERE [Product Number] = @pno AND [EAN] = @material AND [ID User] = @usuario  
    END
END

这个相同,但有更新。

按订单:

CREATE TRIGGER [dbo].[TransferenceOrders_Inner]

   ON  [ENGIE].[dbo].[ENGIE_Orders]

   AFTER UPDATE

AS 

DECLARE @material nvarchar(20)
DECLARE @cant int
DECLARE @cantant int
DECLARE @usr int

DECLARE CURSOR_CANTIDAD CURSOR FOR
SELECT [Cantidad],[Product Number],[ID User] FROM [ENGIE].[dbo].[ENGIE_Order_Content] A, INSERTED I WHERE A.[Numero_Pedido] = I.Numero_Pedido

BEGIN
    SET NOCOUNT ON;
    IF UPDATE([Finalizado])
    BEGIN   
        UPDATE A SET [Finalizado] = I.Finalizado FROM [ENGIE].[dbo].[ENGIE_Orders] A INNER JOIN INSERTED I ON A.dbID = I.dbID;

        INSERT INTO [ENGIE].dbo.[ENGIE_Ended_Orders] ([Numero_Pedido],[ID User],[Fecha de realizacion],[OT],[ID Pedido],[Total Pedido]) 
    (SELECT [dbID],[ID User],[Fecha de realizacion],[OT],[Numero_Pedido],[Total Pedido] FROM INSERTED)

        INSERT INTO [ENGIE].dbo.[ENGIE_Ended_Order_Content] ([Numero_Pedido],[Product Number],[Fecha de realizacion],[Cantidad],[ID Pedido]) 
    (SELECT [Numero_Pedido],[Product Number],[Fecha de realizacion],[Cantidad],[ID Pedido] FROM [ENGIE].[dbo].[ENGIE_Order_Content] A INNER JOIN INSERTED I ON A.Numero_Pedido = I.dbID)

        OPEN CURSOR_CANTIDAD
        FETCH CURSOR_CANTIDAD INTO @cant,@material,@usr
        WHILE (@@FETCH_STATUS = 0)
        BEGIN
            UPDATE ENGIE.dbo.ENGIE_User_Material SET [Cantidad Actual] = (SELECT [Cantidad Actual] FROM ENGIE.dbo.ENGIE_User_Material A,INSERTED I WHERE A.[Product Number] = (SELECT [Product Number] FROM ENGIE.dbo.[ENGIE_Order_Content] B,INSERTED C WHERE B.Numero_Pedido = C.dbID)
         AND A.[ID User] = I.[ID User])-@cant WHERE [Product Number] = @material AND [ID User] = @usr)          
            FETCH CURSOR_CANTIDAD INTO @cant,@material,@usr
        END
        CLOSE CURSOR_CANTIDAD; 
        DELETE A FROM [ENGIE].[dbo].[ENGIE_Orders] A INNER JOIN INSERTED I ON I.[dbID] = A.[dbID] AND A.[ID User] = I.[ID User];
    END
    DEALLOCATE CURSOR_CANTIDAD; 
END 

此触发器需要光标,因为它必须补充刚刚处理的订单内容的数量。

在 Order_Content 上:

ALTER TRIGGER [dbo].[AutoIDOrder_DEL]

ON [ENGIE].[dbo].[ENGIE_Order_Content]

AFTER DELETE

AS

DECLARE @numped int
DECLARE @prod nchar(20)
DECLARE @pvp decimal(38,20)
DECLARE @pvpsdto decimal(38,20)
DECLARE @dto decimal(38,20)
DECLARE @cantidad int

SET @numped = (SELECT Numero_Pedido FROM INSERTED)
SET @prod = (SELECT [Product Number] FROM INSERTED)
SET @cantidad = (SELECT [Cantidad] FROM INSERTED)
SET @pvp = (SELECT [Total Pedido] FROM [ENGIE].[dbo].[ENGIE_Orders] WHERE [dbID] = @numped)

BEGIN
    SET NOCOUNT ON; 
    SET @pvpsdto = (SELECT [PVP Neto] FROM [ENGIE].[dbo].[ENGIE_User_Material] WHERE [Product Number] = @prod)
    SET @dto = (SELECT [% DTO] FROM [ENGIE].[dbo].[ENGIE_User_Material] WHERE [Product Number] = @prod)
    UPDATE [ENGIE].[dbo].[ENGIE_Orders] SET [Total Pedido] = @pvp-(@cantidad*((@pvpsdto*100)/@dto)) WHERE [dbID] = @numped
END

当您从订单内容中删除注册表时,此选项将更新订单中的“订单总计”。

ALTER TRIGGER [dbo].[Qty_Restriction_3]

ON [ENGIE].[dbo].[ENGIE_Order_Content]

INSTEAD OF INSERT

AS

DECLARE @cantAnt int
DECLARE @cantAct int
DECLARE @cant int
DECLARE @pvpsdto decimal(38,20)
DECLARE @dto decimal(38,20)
DECLARE @total decimal(38,20)

SET @cant = (SELECT [Cantidad] FROM INSERTED)
SET @cantAnt = (SELECT [Cantidad Total] FROM [ENGIE].[dbo].[ENGIE_User_Material] WHERE [Product Number] = (SELECT [Product Number] FROM INSERTED) AND [ID User] = (SELECT [ID User] FROM INSERTED))
SET @cantAct = (SELECT [Cantidad Actual] FROM [ENGIE].[dbo].[ENGIE_User_Material]WHERE [Product Number] = (SELECT [Product Number] FROM INSERTED) AND [ID User] = (SELECT [ID User] FROM INSERTED))
SET @pvpsdto = (SELECT [PVP Neto] FROM [ENGIE].[dbo].[ENGIE_User_Material] WHERE [Product Number] = (SELECT [Product Number] FROM INSERTED) AND [ID User] = (SELECT [ID User] FROM INSERTED))
SET @dto = (SELECT [% DTO] FROM [ENGIE].[dbo].[ENGIE_User_Material] WHERE [Product Number] = (SELECT [Product Number] FROM INSERTED) AND [ID User] = (SELECT [ID User] FROM INSERTED))
SET @total = (SELECT [Total Pedido] FROM [ENGIE].[dbo].[ENGIE_Orders] WHERE [dbID] = (SELECT [Numero_Pedido] FROM INSERTED)) 
IF(@total IS NULL) 
BEGIN
    SET @total = 0.0
END

BEGIN
    SET NOCOUNT ON; 
    BEGIN
        IF((@cantAct+@cant) > @cantAnt)
        BEGIN
            RAISERROR('La Cantidad Actual no debe superar la Cantidad Total',18,1);
        END 
        ELSE BEGIN
            INSERT INTO ENGIE.dbo.ENGIE_Order_Content SELECT * FROM INSERTED;
            UPDATE [ENGIE].[dbo].[ENGIE_Order_Content] SET [ID Pedido] = (SELECT [Numero_Pedido] FROM [ENGIE].[dbo].[ENGIE_Orders] WHERE [dbID] LIKE (SELECT [Numero_Pedido] FROM INSERTED)) WHERE [Numero_Pedido] LIKE (SELECT [Numero_Pedido] FROM INSERTED) AND [Product Number] LIKE (SELECT [Product Number] FROM INSERTED);
            UPDATE [ENGIE].[dbo].[ENGIE_Order_Content] SET [Fecha de realizacion] = (SELECT [Fecha de realizacion] FROM [ENGIE].[dbo].[ENGIE_Orders] WHERE [dbID] =  (SELECT [Numero_Pedido] FROM INSERTED)) WHERE [Numero_Pedido] LIKE (SELECT [Numero_Pedido] FROM INSERTED) AND [Product Number] LIKE (SELECT [Product Number] FROM INSERTED);
        ----> AUQUI CALCULO DE TOTAL

            UPDATE [ENGIE].[dbo].[ENGIE_Orders] SET [Total Pedido] = (@total+(@cant*(@pvpsdto-((@pvpsdto*@dto)/100)))) WHERE [dbID] = (SELECT [Numero_Pedido] FROM INSERTED)
        END
    END
END

这将避免在订单内容中插入不符合(实际数量

ALTER TRIGGER [dbo].[Qty_Restriction_4]

ON [ENGIE].[dbo].[ENGIE_Order_Content]

INSTEAD OF UPDATE

AS

DECLARE @cantAnt int
DECLARE @cantAct int
DECLARE @cant int
DECLARE @pvpsdto decimal(38,20)
DECLARE @dto decimal(38,20)
DECLARE @bool bit
DECLARE @total decimal(38,20)

SET @bool = 0;
SET @cant = (SELECT [Cantidad] FROM INSERTED)
SET @userId = (SELECT [ID User] FROM [ENGIE].[dbo].[ENGIE_Orders] WHERE [dbID] = (SELECT [Numero_Pedido] FROM INSERTED)) 
SET @cantAnt = (SELECT [Cantidad Total] FROM [ENGIE].[dbo].[ENGIE_User_Material] WHERE [ID User] = @userId AND [Product Number] = (SELECT [Product Number] FROM INSERTED))
SET @cantAct = (SELECT [Cantidad Actual] FROM [ENGIE].[dbo].[ENGIE_User_Material] WHERE [ID User] = @userId AND [Product Number] = (SELECT [Product Number] FROM INSERTED))
SET @pvpsdto = (SELECT [PVP Neto] FROM [ENGIE].[dbo].[ENGIE_User_Material] WHERE [Product Number] = (SELECT [Product Number] FROM INSERTED) AND [ID User] = (SELECT [ID User] FROM INSERTED))
SET @dto = (SELECT [% DTO] FROM [ENGIE].[dbo].[ENGIE_User_Material] WHERE [Product Number] = (SELECT [Product Number] FROM INSERTED) AND [ID User] = (SELECT [ID User] FROM INSERTED))
SET @total = (SELECT [Total Pedido] FROM [ENGIE].[dbo].[ENGIE_Orders] WHERE [dbID] = (SELECT [Numero_Pedido] FROM INSERTED)) 
IF(@total IS NULL) 
BEGIN
    SET @total = 0.0
END

BEGIN
    SET NOCOUNT ON; 
    IF UPDATE([Cantidad])
    BEGIN
        IF((@cantAct+@cant) > @cantAnt)
        BEGIN
            RAISERROR('La Cantidad Actual no debe superar la Cantidad Total',18,1);
        --->ROLLBACK TRANSACTION;
        END 
        ELSE BEGIN
            UPDATE ENGIE.dbo.ENGIE_Order_Content SET [Cantidad] = (SELECT [Cantidad] FROM inserted) WHERE [Numero_Pedido]= (SELECT [Numero_Pedido] FROM INSERTED) AND [Product Number]= (SELECT [Product Number] FROM INSERTED)
            SET @bool = 1;
        END;
    END;
    IF UPDATE([Procesado])
    BEGIN
        UPDATE ENGIE.dbo.ENGIE_Order_Content SET [Procesado] = (SELECT [Procesado] FROM inserted) WHERE [Numero_Pedido]= (SELECT [Numero_Pedido] FROM INSERTED) AND [Product Number]= (SELECT [Product Number] FROM INSERTED)
    END;
    IF UPDATE([Finalizado])
    BEGIN
        UPDATE ENGIE.dbo.ENGIE_Order_Content SET [Finalizado] = (SELECT [Finalizado] FROM inserted) WHERE [Numero_Pedido]= (SELECT [Numero_Pedido] FROM INSERTED) AND [Product Number]= (SELECT [Product Number] FROM INSERTED)
    END;
    IF UPDATE([ID Pedido])
    BEGIN
        UPDATE ENGIE.dbo.ENGIE_Order_Content SET [ID Pedido] = (SELECT [ID Pedido] FROM inserted) WHERE [Numero_Pedido]= (SELECT [Numero_Pedido] FROM INSERTED) AND [Product Number]= (SELECT [Product Number] FROM INSERTED)
    END;
---> AQUI METER LO DEL AFTER UPDATE, PARA QUE NO HAYA PROBLEMA, CON UN BOOLEAN
    IF (@bool = 1) 
    BEGIN
    SET NOCOUNT ON; 
        UPDATE [ENGIE].[dbo].[ENGIE_Orders] SET [Total Pedido] = (@total+(@cant*(@pvpsdto-((@pvpsdto*@dto)/100)))) WHERE [dbID] = (SELECT [Numero_Pedido] FROM INSERTED)
    END;
END

最后一个假装与插入相同,但在更新时,使实际数量的限制起作用并为 [Order Total] 列赋予价值

【问题讨论】:

  • 写一个存储过程
  • 您使用的是sql-server 还是plsql?请删除不相关的标签。
  • 完成,它的 tsql 没有 plsql。
  • @BorisPomarol ,第二次编辑约束不能用于此。如果[实际数量]

标签: sql-server tsql


【解决方案1】:

您的触发器不处理多行插入。如果

SET @usuario = (SELECT [ID User] FROM INSERTED)

返回多个值。它会失败。触发器内的光标是非常糟糕的事情。您应该使用基于集合的方法重新编写触发器 你的前 4 个语句应该重写

UPDATE A
SET [Finalizado] = i.Finalizado
FROM [ENGIE].[dbo].[ENGIE_Orders] A
INNER JOIN INSERTED I ON a.dbID = i.dbid;

INSERT INTO [ENGIE].dbo.[ENGIE_Ended_Orders] (
    [Numero_Pedido]
    ,[ID User]
    ,[Fecha de realizacion]
    ,[OT]
    ,[ID Pedido]
    )
SELECT [dbID]
    ,[ID User]
    ,[Fecha de realizacion]
    ,[OT]
    ,[Numero_Pedido]
FROM [ENGIE].[dbo].[ENGIE_Orders]
FROM inserted

INSERT INTO [ENGIE].dbo.[ENGIE_Ended_Order_Content] (
    [Numero_Pedido]
    ,[Product Number]
    ,[Fecha de realizacion]
    ,[Cantidad]
    ,[ID Pedido]
    )
SELECT [Numero_Pedido]
    ,[Product Number]
    ,[Fecha de realizacion]
    ,[Cantidad]
    ,[ID Pedido]
FROM [ENGIE].[dbo].[ENGIE_Order_Content] A
INNER JOIN INSERTED I ON A.Numero_Pedido = I.Dbid

DELETE A
FROM [ENGIE].[dbo].[ENGIE_Orders] A
INNER JOIN INSERTED I ON
WHERE I.[dbID] = A.DBID
    AND A.[ID User] = I.USERID;

我不确定我是否符合你的标准(特别是连接,请检查一下),但触发器应该用基于集合的逻辑重写,我希望光标也可以被消除

【讨论】:

  • 早上,如果我想在自己的表中触发一个不会更新实际数量的情况,除非它低于或等于总数量,我将如何使用算术运算?不能使用:INSTEAD OF UPDATE... AS.... ... IF SELECT [Cantidad Actual] FROM INSERTED > SELECT [Cantidad Total] FROM INSERTED , BEGIN ---> RAISE ERROR END ELSE BEGIN -> PERFORM UPDATE END 我怎样才能绕过这个问题?
  • @BorisPomarol ,你能在列上为此添加一个约束吗?
  • 在 Dynamics NAV 中使用 C/AL 到纯 T-SQL 的转变让我在某些方面受苦,核心是我不明白我可以用 T 做的所有事情-SQL。所以,是的,我可以在这里设置一个约束。那么我应该放 2 个,一个放在实际数量上,另一个放在总数量上,对吗?以及如何告诉约束根据其自身来限制另一个字段的值? (这有点令人困惑)。谢谢,
  • @BorisPomarol 所以你只能拥有纯 tsql 没有存储的 procs 对吗?你能用新的要求更新问题吗?在列中保留约束,因为如果满足一定数量,您的要求就会更新。
  • 纯 T-SQL 是的,我已经在主要问题中对其进行了编辑。但如果是通过约束,我们更容易接近,我们会以这种方式抑制 2 个触发器
猜你喜欢
  • 2018-05-27
  • 2012-07-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多