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
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.