How is this script updating table when using LEFT JOINs?

后端 未结 1 1212
旧时难觅i
旧时难觅i 2020-12-06 14:26

The result in this script is correct, but I don\'t seem to understand why column BetaStatus remains \'NOK\'

Regarding \'Beta\', the first row (Beta = NOK) would upda

相关标签:
1条回答
  • 2020-12-06 15:04

    Let's see what return select instead of update

    select 
         AlphaStatus = ISNULL(rA.Pstatus, AlphaStatus),
         BetaStatus  = ISNULL(rB.Pstatus,  BetaStatus),
         GammaStatus = ISNULL(rG.Pstatus, GammaStatus)
    from @Summary t
    left join @testTable rA on rA.id = t.ID AND rA.Pgroup = 'Alpha'
    left join @testTable rB on rB.id = t.ID AND rB.Pgroup = 'Beta'
    left join @testTable rG on rG.id = t.ID AND rG.Pgroup = 'Gamma'
    

    Result:

    AlphaStatus     BetaStatus  GammaStatus
    OK              NOK         OK
    OK              OK          OK
    OK              OK          OK
    

    Now you try to do UPDATE

    update @Summary 
    set
        AlphaStatus = ISNULL(rA.Pstatus, AlphaStatus),
        BetaStatus  = ISNULL(rB.Pstatus,  BetaStatus),
        GammaStatus = ISNULL(rG.Pstatus, GammaStatus)
    from @Summary t
    left join @testTable rA on rA.id = t.ID AND rA.Pgroup = 'Alpha'
    left join @testTable rB on rB.id = t.ID AND rB.Pgroup = 'Beta'
    left join @testTable rG on rG.id = t.ID AND rG.Pgroup = 'Gamma'
    

    After update table @Summary contains:

    id  AlphaStatus     BetaStatus  GammaStatus
    3   OK              NOK         OK
    

    I suppose you wanted to get:

    id  AlphaStatus     BetaStatus  GammaStatus
    3   OK              OK      OK
    

    But UPDATE doesn't work that way, when they are multiple matches the result may be inconsistent and it is partially based on the table ordering or actual execution plan.

    See also: Let's deprecate UPDATE FROM! by Hugo Kornelis

    Correctness? Bah, who cares?

    Well, most do. That’s why we test.

    If I mess up the join criteria in a SELECT query so that too many rows from the second table match, I’ll see it as soon as I test, because I get more rows back then expected. If I mess up the subquery criteria in an ANSI standard UPDATE query in a similar way, I see it even sooner, because SQL Server will return an error if the subquery returns more than a single value. But with the proprietary UPDATE FROM syntax, I can mess up the join and never notice – SQL Server will happily update the same row over and over again if it matches more than one row in the joined table, with only the result of the last of those updates sticking. And there is no way of knowing which row that will be, since that depends in the query execution plan that happens to be chosen. A worst case scenario would be one where the execution plan just happens to result in the expected outcome during all tests on the single-processor development server – and then, after deployment to the four-way dual-core production server, our precious data suddenly hits the fan…

    Too see this inconsistency instead of table variable use table and create clustered indexes:

    SqlFiddleDemo

    CREATE TABLE testTable(id int,
        Pgroup varchar(10),
        Pstatus varchar(3));
    
    CREATE CLUSTERED INDEX clx_name
    ON  testTable(PStatus DESC);
    
    /* or */
    CREATE CLUSTERED INDEX clx_name
    ON  testTable(PStatus ASC);  
    

    If you use MERGE for instance:

    ;WITH cte as
    (SELECT 
       ra.id
       ,AlphaStatus = rA.Pstatus
       ,BetaStatus = rB.Pstatus
       ,GammaStatus = rG.Pstatus
    from @Summary t
    left join @testTable rA on rA.id = t.ID AND rA.Pgroup = 'Alpha'
    left join @testTable rB on rB.id = t.ID AND rB.Pgroup = 'Beta'
    left join @testTable rG on rG.id = t.ID AND rG.Pgroup = 'Gamma'
    )
    MERGE @Summary AS TGT
    USING (SELECT * FROM cte ) AS  SRC
       ON TGT.id = SRC.id
    WHEN MATCHED THEN
       UPDATE
       SET 
        AlphaStatus = ISNULL(src.AlphaStatus, tgt.AlphaStatus),
        BetaStatus  = ISNULL(src.BetaStatus,  tgt.BetaStatus),
        GammaStatus = ISNULL(src.GammaStatus, tgt.GammaStatus);
    

    You will get clear error message that this is not allowed:

    The MERGE statement attempted to UPDATE or DELETE the same row more than once. This happens when a target row matches more than one source row. A MERGE statement cannot UPDATE/DELETE the same row of the target table multiple times. Refine the ON clause to ensure a target row matches at most one source row, or use the GROUP BY clause to group the source rows.

    0 讨论(0)
提交回复
热议问题