Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/26.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql server 插入和更新时的SQL Server触发器_Sql Server - Fatal编程技术网

Sql server 插入和更新时的SQL Server触发器

Sql server 插入和更新时的SQL Server触发器,sql-server,Sql Server,我希望创建一个SQL Server触发器,如果记录符合特定条件,它会将记录从一个表移动到相同的副本表 问题:我需要指定每一列,还是可以使用通配符 我可以使用类似于: SET @RecID = (SELECT [RecoID] FROM Inserted) IF NULLIF(@RecID, '') IS NOT NULL (then insert....) 谢谢 你可以,但我建议你不要这样做。如果您的源表发生了更改,那么将开始失败 此外,在您的示例中,如果一次插入多行,您将抛出一个错

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

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

我可以使用类似于:

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

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

谢谢

你可以,但我建议你不要这样做。如果您的源表发生了更改,那么将开始失败

此外,在您的示例中,如果一次插入多行,您将抛出一个错误(或产生不可预测的结果)。我推荐一种更基于集合的方法:

   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;
创建触发器\u名称
在桌上
而不是插入
作为
开始
--确保我们没有从另一个触发器触发我们自己

如果TRIGGER_NESTLEVEL()你可以在触发器中“做”很多事情,但这并不意味着你应该这样做。我强烈要求不惜一切代价避免在触发器中设置标量变量。即使您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;

使用标量选项,我如何简单地移动记录,它不需要保留在源表中。另外,你能解释一下ISNULL(i.Pt\u ID,d.Pt\u ID)吗?无论如何,我都不会使用标量选项。。。如果要从源表中删除,我将在存储过程中处理,而不是在触发器中处理。我不希望一个表上的临时维护会导致另一个表中的数据被意外删除。事实上,我主张使用触发器的用例很少。。。比如维护审核历史记录,或者确保两个不同环境中的两个表与其中一个保持同步。就是这样。至于ISNULL(i.Pt\u ID,d.Pt\u ID)。。。UPDATE语句生成2个表(插入和删除)。。。在我的示例中,我只想为每个ID生成一个审计行,所以我在插入的ID和删除的ID上使用了完全连接(在I.Pt_ID=d.Pt_ID上使用了完全连接删除的d),而ISNULL(I.Pt_ID,d.Pt_ID)只是说,“如果这是一个插入,请使用i.Pt_ID;如果这是一个删除,请使用d.Pt_ID;如果这是一个更新,我不在乎哪一个,因为它们的值相同“。第一个表被用作暂存表,发生的情况是插入一条记录,然后进行多次更新。在最终更新时,想法是将该记录移动到主DB表中,并将其从暂存表中删除。这是因为主表有超过1亿条记录,并且每次更新记录都会产生其他副作用。因此,总之,我们希望确保记录在移动到主表之前是“最终的”。@user3255066-即使在这种情况下,我仍然不相信装配工中的标量变量。您对该过程的假设可能在99%的时间内有效,但墨菲定律将找到执行多行多行事务的方法。一旦发生这种情况,您要么丢失了数据,要么在系统中引入了异常行为。你为什么要冒险?标量触发器绝对没有优势。多行版本可以处理单行DML,就像处理标量版本一样,并且将在那里。如果想法是移动记录,是否需要左连接?您建议如何删除插入的\更新的记录?