Sql server 避免使用游标使用触发器更新多个记录

Sql server 避免使用游标使用触发器更新多个记录,sql-server,sql-server-2008,triggers,cursor,Sql Server,Sql Server 2008,Triggers,Cursor,我有一张有一百万张唱片的桌子。最初,我的表是空的,但我使用批量插入将这些记录添加到数据库中。我在插入后有一个触发器,用于更新此表中的initialValue字段。initialValue的值是对另一个表(my\u data\u db)中的特定变量的计算,该表汇总了所有记录。我正在使用v1、v2等列的值作为表my_data_db中的列名 我知道这是一种糟糕的做法,但目前我知道如何计算每一行的唯一方法是使用光标。显然有一百万张唱片,这真的很慢 下面是我使用触发器的表的示例: TABLE: test3

我有一张有一百万张唱片的桌子。最初,我的表是空的,但我使用
批量插入
将这些记录添加到数据库中。我在插入后有一个
触发器,用于更新此表中的
initialValue
字段。
initialValue
的值是对另一个表(
my\u data\u db
)中的特定变量的计算,该表汇总了所有记录。我正在使用
v1
v2
等列的值作为表
my_data_db
中的列名

我知道这是一种糟糕的做法,但目前我知道如何计算每一行的唯一方法是使用光标。显然有一百万张唱片,这真的很慢

下面是我使用触发器的表的示例:

TABLE: test3
rowID    v1      v2      v3       combo   initialValue
1        NULL    M170_3  M170_4   C       NULL
2        M170_2  M170_3  M170_4   ABC     NULL
3        M170_2  M170_3  NULL     AB      NULL
...
我的触发器是:

CREATE TRIGGER [dbo].[trig_UPDATE_test3] 
ON [dbo].[test3] 

AFTER INSERT 
AS 
Begin 

DECLARE @sql VARCHAR(MAX) 
DECLARE @v1 VARCHAR(20) 
DECLARE @v2 VARCHAR(20) 
DECLARE @v3 VARCHAR(20) 
DECLARE @combo VARCHAR(30) 
DECLARE mycursor CURSOR FOR     
    SELECT v1, v2, v3, combo 
    FROM Inserted 

    OPEN mycursor 
    FETCH NEXT FROM mycursor INTO @v1, @v2, @v3, @combo 
    WHILE @@FETCH_STATUS = 0 
    BEGIN 
        IF( @v1 IS NOT NULL OR @v2 IS NOT NULL OR @v3 IS NOT NULL) 
        BEGIN 
            SET @sql = 'DECLARE @finalValue DECIMAL(18, 15);' 
            SET @sql = @sql + 'UPDATE test3 Set initialValue = (SELECT CAST(SUM(' 

            IF(@v1 IS NOT NULL) 
            BEGIN 
                SET @sql = @sql + 'CASE ' + @v1 + ' WHEN 1 THEN 1 WHEN 2 THEN .75 WHEN 3 THEN .25 WHEN 4 THEN .1 END * ' 
            END 

            IF(@v2 IS NOT NULL) 
            BEGIN 
                SET @sql = @sql + 'CASE ' + @v2 + ' WHEN 1 THEN 1 WHEN 2 THEN .75 WHEN 3 THEN .25 WHEN 4 THEN .1 END * ' 
            END 

            IF(@v3 IS NOT NULL) 
            BEGIN 
                SET @sql = @sql + 'CASE ' + @v3 + ' WHEN 1 THEN 1 WHEN 2 THEN .75 WHEN 3 THEN .25 WHEN 4 THEN .1 END * ' 
            END 

            SET @sql = @sql + 'RESP_WEIGHT / 4898.947426) AS FLOAT) FROM dbo.my_data_db) WHERE combo = ''' + @combo + ''';' 

            EXECUTE(@sql) 

        END 
        FETCH NEXT FROM mycursor INTO @v1, @v2, @v3, @combo 
    END 
    CLOSE mycursor 
    DEALLOCATE mycursor 
End
在触发器运行之后,我的
test3
表将如下所示:

TABLE: test3
rowID    v1      v2      v3       combo   initialValue
1        NULL    M170_3  M170_4   C       0.138529
2        M170_2  M170_3  M170_4   ABC     0.683190
3        M170_2  M170_3  NULL     AB      0.014923
...

有没有一种不用光标就能完成的方法?

是的。您可以在
批量插入后使用单个
更新-FROM
语句执行此操作:

UPDATE t3 SET initialValue = t.mySum
FROM test3 t3
CROSS APPLY (SELECT SUM(
       CASE t3.v1 WHEN 'M170_2' THEN CASE d.M170_2 
             WHEN 1 THEN 1 
             WHEN 2 THEN .75 
             WHEN 3 THEN .25 
             WHEN 4 THEN .1 
       ELSE 1 END END * 
       CASE t3.v1 WHEN 'M170_3' THEN CASE d.M170_3 
             WHEN 1 THEN 1 
             WHEN 2 THEN .75 
             WHEN 3 THEN .25 
             WHEN 4 THEN .1 
       ELSE 1 END END * 
       CASE t3.v1 WHEN 'M170_4' THEN CASE d.M170_4 
             WHEN 1 THEN 1 
             WHEN 2 THEN .75 
             WHEN 3 THEN .25 
             WHEN 4 THEN .1 
       ELSE 1 END END * 
       d.RESP_WEIGHT / 4898.947426) as mySum 
       FROM my_data_db d WHERE d.combo = t3.combo) t
WHERE t3.v1 IS NOT NULL OR t3.v2 IS NOT NULL OR t3.v3 IS NOT NULL
要从触发器执行此操作,您需要进行一些小更改:

UPDATE t3 SET initialValue = t.mySum
FROM test3 t3
-- Here's the change
INNER JOIN inserted i ON i.RowID = t3.RowID
CROSS APPLY (SELECT SUM(
       CASE t3.v1 WHEN 'M170_2' THEN CASE d.M170_2 
             WHEN 1 THEN 1 
             WHEN 2 THEN .75 
             WHEN 3 THEN .25 
             WHEN 4 THEN .1 
       ELSE 1 END END * 
       CASE t3.v1 WHEN 'M170_3' THEN CASE d.M170_3 
             WHEN 1 THEN 1 
             WHEN 2 THEN .75 
             WHEN 3 THEN .25 
             WHEN 4 THEN .1 
       ELSE 1 END END * 
       CASE t3.v1 WHEN 'M170_4' THEN CASE d.M170_4 
             WHEN 1 THEN 1 
             WHEN 2 THEN .75 
             WHEN 3 THEN .25 
             WHEN 4 THEN .1 
       ELSE 1 END END * 
       d.RESP_WEIGHT / 4898.947426) as mySum 
       FROM my_data_db d WHERE d.combo = t3.combo) t
WHERE t3.v1 IS NOT NULL OR t3.v2 IS NOT NULL OR t3.v3 IS NOT NULL

谢谢在
my_data\u db
中,我实际上没有名为“combo”的列。我有一个名为“M170_2”的列(参见上面的示例),它是
test3
中列的值(例如“v1”),我使用它来计算
my_data_db
中的哪些列。如果我的两个表完全不相关,有没有办法做到这一点?触发器中的游标非常糟糕。锁、回滚、死锁,整个世界都很容易发生错误行为。不要害怕用超级创意来摆脱它们。@tptcat:Ohhh!我现在明白了。我想知道你为什么要构造一个动态sql语句。。。我做了一些修改来修复它,现在可以用了吗?@迭戈:如果我不清楚的话,很抱歉。实际上,我捕获了该部分并修复了它,但问题是在
my_data\u db
中没有名为“combo”的列,因此
其中d.combo=t3。combo
无效。这有意义吗?@Diego:还有-只有当
my_data\u db
中的所有三列都不为空时,才会进行计算。例如,如果M170_4为空,我需要它来计算M170_2和M170_3。我尝试过的所有方法都只在3列都不为空时才考虑。