避免使用触发器更新许多记录的游标

时间:2012-03-09 20:26:25

标签: sql-server sql-server-2008 triggers cursor

我有一张表只有超过100万条记录。最初,我的表是空的,但我使用BULK INSERT将这些记录添加到数据库中。我有一个AFTER INSERT触发器,用于更新此表中的initialValue字段。 initialValue的值是对所有记录中的另一个表(my_data_db)中的特定变量的计算。我使用v1v2等列的值作为表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
...

有没有办法可以在不使用光标的情况下完成此操作?

1 个答案:

答案 0 :(得分:2)

是。您可以在UPDATE - FROM

之后使用单个BULK INSERT语句执行此操作
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