插入和更新上的SQL Server触发器

时间:2017-10-03 01:00:22

标签: sql-server

我希望创建一个SQL Server触发器,如果​​记录符合特定条件,则会将记录从一个表移动到相同的副本表。

问题:我是否需要指定每列,还是可以使用通配符?

我可以使用类似的东西:

SET @RecID = (SELECT [RecoID] FROM Inserted)

IF NULLIF(@RecID, '') IS NOT NULL
    (then insert....)

谢谢!

2 个答案:

答案 0 :(得分:1)

你在触发器中“可以”做很多事情,但这并不意味着你应该这样做。我会敦促不惜一切代价避免在触发器中设置标量变量。即使你100%确定你的表永远不会有更多的每行插入1行,因为这就是应用程序的设计方式......当你发现不是所有的交易都来自应用程序时,你会陷入非常粗鲁的觉醒。

以下是两种触发器的快速演示......

USE tempdb;
GO

IF OBJECT_ID('tempdb.dbo.PrimaryTable', 'U') IS NOT NULL 
DROP TABLE dbo.PrimaryTable;
GO 
IF OBJECT_ID('tempdb.dbo.TriggerScalarLog', 'U') IS NOT NULL 
DROP TABLE dbo.TriggerScalarLog;
GO  
IF OBJECT_ID('tempdb.dbo.TriggerMultiRowLog', 'U') IS NOT NULL 
DROP TABLE dbo.TriggerMultiRowLog;
GO

CREATE TABLE dbo.PrimaryTable (
    Pt_ID INT NOT NULL IDENTITY (1,1) PRIMARY KEY CLUSTERED,
    Col_1 INT NULL,
    Col_2 DATE NOT NULL 
        CONSTRAINT df_Col2 DEFAULT (GETDATE())
    );
GO 
CREATE TABLE dbo.TriggerScalarLog (
    Pt_ID INT,
    Col1_Old INT,
    Col1_New INT,
    Col2_Old DATE,
    Col2_New DATE
    );
GO 
CREATE TABLE dbo.TriggerMultiRowLog (
    Pt_ID INT,
    Col1_Old INT,
    Col1_New INT,
    Col2_Old DATE,
    Col2_New DATE
    );
GO 

--=======================================================

CREATE TRIGGER dbo.PrimaryCrudScalar ON dbo.PrimaryTable
AFTER INSERT, UPDATE, DELETE 
AS 
    SET NOCOUNT ON;
    DECLARE 
        @Pt_ID INT,
        @Col1_Old INT,
        @Col1_New INT,
        @Col2_Old DATE,
        @Col2_New DATE;

    SELECT 
        @Pt_ID = ISNULL(i.Pt_ID, d.Pt_ID),
        @Col1_Old = d.Col_1,
        @Col1_New = i.Col_1,
        @Col2_Old = d.Col_2,
        @Col2_New = i.Col_2
    FROM 
        Inserted i
        FULL JOIN Deleted d
            ON i.Pt_ID = d.Pt_ID;

    INSERT dbo.TriggerScalarLog (Pt_ID, Col1_Old, Col1_New, Col2_Old, Col2_New) 
    VALUES (@Pt_ID, @Col1_Old, @Col1_New, @Col2_Old, @Col2_New);
GO -- DROP TRIGGER dbo.PrimaryCrudScalar; 

CREATE TRIGGER PrimaryCrudMultiRow ON dbo.PrimaryTable
AFTER INSERT, UPDATE, DELETE 
AS 
    SET NOCOUNT ON;

    INSERT dbo.TriggerMultiRowLog (Pt_ID, Col1_Old, Col1_New, Col2_Old, Col2_New)
    SELECT 
        ISNULL(i.Pt_ID, d.Pt_ID),
        d.Col_1,
        i.Col_1,
        d.Col_2,
        i.Col_2
    FROM 
        Inserted i
        FULL JOIN Deleted d
            ON i.Pt_ID = d.Pt_ID;
GO -- DROP TRIGGER dbo.TriggerMultiRowLog;

--=======================================================
--=======================================================

-- --insert test...
INSERT dbo.PrimaryTable (Col_1)
SELECT TOP 100
    o.object_id
FROM
    sys.objects o;

SELECT 'INSERT Scarar results';
SELECT * FROM dbo.TriggerScalarLog tsl;
SELECT 'INSERT Multi-Row results';
SELECT * FROM dbo.TriggerMultiRowLog tmrl;

UPDATE pt SET 
    pt.Col_1 = pt.Col_1 + rv.RandomVal,
    pt.Col_2 = DATEADD(DAY, rv.RandomVal, pt.Col_2)
FROM
    dbo.PrimaryTable pt
    CROSS APPLY ( VALUES (ABS(CHECKSUM(NEWID())) % 10000 + 1) ) rv (RandomVal);

SELECT 'UPDATE Scarar results';
SELECT * FROM dbo.TriggerScalarLog tsl;
SELECT 'UPDATE Multi-Row results';
SELECT * FROM dbo.TriggerMultiRowLog tmrl;

DELETE pt
FROM
    dbo.PrimaryTable pt;

SELECT 'DELETE Scarar results';
SELECT * FROM dbo.TriggerScalarLog tsl;
SELECT 'DELETE Multi-Row results';
SELECT * FROM dbo.TriggerMultiRowLog tmrl;

答案 1 :(得分:0)

你可以,但我建议不要这样做。如果你的源表发生了变化,事情就会开始失败。

此外,在您的示例中,如果您一次插入多行,则会引发错误(或者具有不可预测的结果)。我建议采用更基于集合的方法:

   INSERT table2 (   user_id ,
              user_name ,
              RecoID
          )
   SELECT user_id ,
          user_name ,
          RecoID
   FROM   inserted i
          LEFT JOIN table2 t ON i.RecoID = t.RecoID
   WHERE  t.RecoID IS NULL;

编辑:

如果你想停止在原始表格上发生插入,那么你需要做一些以下的事情:

CREATE TRIGGER trigger_name
ON table_orig
INSTEAD OF INSERT
AS
    BEGIN

    -- make sure we aren't triggering from ourselves from another trigger
    IF TRIGGER_NESTLEVEL() <= 1
        return;

        -- insert into the table_copy if the inserted row is already in table_orig (not null)
        INSERT table_copy (   user_id ,
                              user_name ,
                              RecoID
                          )
           SELECT user_id ,
                  user_name ,
                  RecoID
           FROM   inserted i
                  LEFT JOIN table_orig c ON i.RecoID = c.RecoID
           WHERE  t.RecoID IS NOT NULL;

        -- insert into table_orig if the inserted row is not already in table_orig (null)
        INSERT table_orig (   user_id ,
                              user_name ,
                              RecoID
                          )
               SELECT user_id ,
                      user_name ,
                      RecoID
               FROM   inserted i
                      LEFT JOIN table_orig c ON i.RecoID = c.RecoID
               WHERE  t.RecoID IS NULL;
    END;

instead of如果您不想实际插入插件,将停止插入,因此您需要自己执行此操作(第二个插入语句)。

请注意我已将部分nulls更改为not nulls,并在某些情况下保留了我们要加入的表格。