触发锁定表计算运行总计

时间:2016-05-26 07:45:52

标签: sql-server tsql triggers

我在SQL Server 2012中的表上有以下触发器:

CREATE TRIGGER [dbo].[UpdateTotals] 
   ON  [dbo].[DEC10assessmentData] 
   AFTER UPDATE
AS 
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

    --update list pract test 1 total
    if update (list_practTest1_s1) or update (list_practTest1_s2)
    begin
        update DEC10assessmentData
        set list_practTest1_total = CAST(list_practTest1_s1 AS decimal(3 , 1)) + CAST(list_practTest1_s2 AS decimal(3 , 1))
        where 
            TRY_CONVERT(decimal(3 , 1) , dbo.DEC10assessmentData.list_practTest1_s1) IS NOT NULL 
            and TRY_CONVERT(decimal(3 , 1) , dbo.DEC10assessmentData.list_practTest1_s2) IS NOT NULL
        update DEC10assessmentData
        set list_practTest1_total = NULL
        where 
            TRY_CONVERT(decimal(3 , 1) , dbo.DEC10assessmentData.list_practTest1_s1) IS NULL 
            or TRY_CONVERT(decimal(3 , 1) , dbo.DEC10assessmentData.list_practTest1_s2) IS NULL
    end

    --update list test 1 total
    if UPDATE (list_test1_s1) or update (list_test1_s2)
    begin
        update DEC10assessmentData
        set list_test1_total = CAST(list_test1_s1 AS decimal(3 , 1)) + CAST(list_test1_s2 AS decimal(3 , 1))
        where 
            TRY_CONVERT(decimal(3 , 1) , dbo.DEC10assessmentData.list_test1_s1) IS NOT NULL 
            and TRY_CONVERT(decimal(3 , 1) , dbo.DEC10assessmentData.list_test1_s2) IS NOT NULL
        update DEC10assessmentData
        set list_test1_total = NULL
        where 
            TRY_CONVERT(decimal(3 , 1) , dbo.DEC10assessmentData.list_test1_s1) IS NULL 
            or TRY_CONVERT(decimal(3 , 1) , dbo.DEC10assessmentData.list_test1_s2) IS NULL
    end

    --update list pract test 2 total
    if update (list_practTest2_s1) or update (list_practTest2_s2)
    begin
        update DEC10assessmentData
        set list_practTest2_total = CAST(list_practTest2_s1 AS decimal(3 , 1)) + CAST(list_practTest2_s2 AS decimal(3 , 1))
        where 
            TRY_CONVERT(decimal(3 , 1) , dbo.DEC10assessmentData.list_practTest2_s1) IS NOT NULL 
            and TRY_CONVERT(decimal(3 , 1) , dbo.DEC10assessmentData.list_practTest2_s2) IS NOT NULL
        update DEC10assessmentData
        set list_practTest2_total = NULL
        where 
            TRY_CONVERT(decimal(3 , 1) , dbo.DEC10assessmentData.list_practTest2_s1) IS NULL 
            or TRY_CONVERT(decimal(3 , 1) , dbo.DEC10assessmentData.list_practTest2_s2) IS NULL
    end

    --update list test 2 total
    if UPDATE (list_test2_s1) or update (list_test2_s2)
    begin
        update DEC10assessmentData
        set list_test2_total = CAST(list_test2_s1 AS decimal(3 , 1)) + CAST(list_test2_s2 AS decimal(3 , 1))
        where 
            TRY_CONVERT(decimal(3 , 1) , dbo.DEC10assessmentData.list_test2_s1) IS NOT NULL 
            and TRY_CONVERT(decimal(3 , 1) , dbo.DEC10assessmentData.list_test2_s2) IS NOT NULL
        update DEC10assessmentData
        set list_test2_total = NULL
        where 
            TRY_CONVERT(decimal(3 , 1) , dbo.DEC10assessmentData.list_test2_s1) IS NULL 
            or TRY_CONVERT(decimal(3 , 1) , dbo.DEC10assessmentData.list_test2_s2) IS NULL
    end

    --update read total
    update DEC10assessmentData
    set read_total = ((read_test1_Scaled / 100 * 8) + (read_test2_scaled / 100 * 12)) / 20 * 100
    where
        read_test1_Scaled is not null and read_test2_scaled is not null

    --update read total to null where scores don't exist
    update DEC10assessmentData
    set read_total = NULL
    where
        read_test1_Scaled is null or read_test2_scaled is null

    --update write total
    update DEC10assessmentData
    set writ_total = (CAST(writ_literatureReview as decimal(3,1)) / 100 * 4 
                        + CAST(writ_exposition as decimal(3,1)) / 100 * 8 
                        + CAST(writ_groupReport as decimal(3,1)) / 100 * 8 
                        + cast(writ_synthSummary as decimal(3,1)) / 100 * 8 
                        + cast(writ_critEvaluation as decimal(3,1)) / 100 * 12) / 40 * 100
    where
        TRY_CONVERT(decimal(3 , 1) , writ_literatureReview) is not null and
        TRY_CONVERT(decimal(3 , 1) , writ_exposition) is not null and
        TRY_CONVERT(decimal(3 , 1) , writ_groupReport) is not null and
        TRY_CONVERT(decimal(3 , 1) , writ_synthSummary) is not null and
        TRY_CONVERT(decimal(3 , 1) , writ_critEvaluation) is not null

    --update write total where scores don't exist
    update DEC10assessmentData
    set writ_total = NULL
    where
        TRY_CONVERT(decimal(3 , 1) , writ_literatureReview) is null or
        TRY_CONVERT(decimal(3 , 1) , writ_exposition) is null or
        TRY_CONVERT(decimal(3 , 1) , writ_groupReport) is null or
        TRY_CONVERT(decimal(3 , 1) , writ_synthSummary) is null or
        TRY_CONVERT(decimal(3 , 1) , writ_critEvaluation) is null

    --update list total
    update DEC10assessmentData
    set list_total = ((list_test1_scaled / 100 * 8) + (list_test2_scaled / 100 * 12)) / 20 * 100
    where
        list_test1_scaled is not null and list_test2_scaled is not null

    --update list total to null where scores don't exist
    update DEC10assessmentData
    set list_total = NULL
    where
        list_test1_scaled is null or list_test2_scaled is null

    --update speak total
    update DEC10assessmentData
    set speak_total = (cast(speak_groupPres as decimal(3,1)) / 100 * 4
                        + CAST(speak_indivPres as decimal(3,1)) / 100 * 8
                        + cast(speak_tutorialDiscus as decimal(3,1)) / 100 * 8) / 20 * 100
    where
        TRY_CONVERT(decimal(3 , 1) , speak_groupPres) is not null and
        TRY_CONVERT(decimal(3 , 1) , speak_indivPres) is not null and
        TRY_CONVERT(decimal(3 , 1) , speak_tutorialDiscus) is not null

    --update speak total where scores don't exist to null
    update DEC10assessmentData
    set speak_total = NULL
    where
        TRY_CONVERT(decimal(3 , 1) , speak_groupPres) is null or
        TRY_CONVERT(decimal(3 , 1) , speak_indivPres) is null or
        TRY_CONVERT(decimal(3 , 1) , speak_tutorialDiscus) is null

    --update overall score
    update DEC10assessmentData
    set overall_total = (read_total + writ_total * 2 + list_total + speak_total) / 5

    --update rec/not rec's for skills and overall
    update DEC10assessmentData
    set read_rec = t.rec_read, writ_rec = t.rec_writ, list_rec = t.rec_list, speak_rec = t.rec_speak, overall_rec = t.rec_overall
    from dbo.udf_getDEC10RecSkillAndOverall() as t inner join DEC10assessmentData
    on t.studentID = DEC10assessmentData.studentID and t.assessmentLookup = DEC10assessmentData.assessmentLookup

    --update rec/not rec's for final rec
    update DEC10assessmentData
    set final_rec = t.rec_final
    from dbo.udf_getDEC10RecFinal() as t inner join DEC10assessmentData
    on t.studentID = DEC10assessmentData.studentID and t.assessmentLookup = DEC10assessmentData.assessmentLookup

END

GO

在获取表中其他列的更新后,我会触发计算表中的运行总计。正如您所看到的,遗憾的是很多varchar值的转换,但除了一个问题 - 锁定之外它还能完成这项工作。

我间歇性地让用户抱怨他们无法执行更新,这似乎是因为触发器锁定了表。有什么方法可以避免这种情况,我如何确认这是发生了什么?他们没有更新触发器更新的列。

我使用Erland Sommarskog的beta_lockinfo获得了两个死锁更新的以下输出:

rsctype locktype lstatus ownertype    waittime spid waittype
KEY         U   WAIT    TRANSACTION     2.192       LCK_M_U 69
DATABASE    S   grant   STW                     69
KEY         X   grant   TRANSACTION             69
OBJECT      IX  grant   TRANSACTION             69
PAGE        IU  grant   TRANSACTION             69
PAGE        IX  grant   TRANSACTION             69
KEY         U   WAIT    TRANSACTION     2.188       LCK_M_U 89
DATABASE    S   grant   STW                     89
KEY         X   grant   TRANSACTION             89
OBJECT      IX  grant   TRANSACTION             89
PAGE        IU  grant   TRANSACTION             89
PAGE        IX  grant   TRANSACTION             89

1 个答案:

答案 0 :(得分:1)

第一个建议应该是不要使用触发器并将业务逻辑移动到其他层。

您的更新会影响整个表,这可能会产生表锁:

update DEC10assessmentData
set overall_total = (read_total + writ_total * 2 + list_total + speak_total) / 5 
etc

为什么要更新除已更改的行之外的其他行? How to work only on the updated row

为了您的计算,use calculated columns instead

你真的需要做所有转换吗?您不能在表定义中修复数据类型吗?

但是,如果这是你绝对与触发器有关的事情,那么也许你可以重构你的表,这样你就不会将数据存储在同一个表中,而是存储在两个表中。这样,用户就可以更新他们的数据,并且可以更新您的数据。