使用LEFT JOIN时,此脚本如何更新表?

fdk*_*fsf 4 sql t-sql

这个脚本的结果是正确的,但我似乎不明白为什么列BetaStatus仍然是'NOK'

关于'Beta',第一行(Beta = NOK)将更新@Summary.BetaStatus为NOK.但后来我认为最后两行@testTable会将BetaStatus从NOK更新回OK.我只是想确定'NOK'实际上是要处理的最后一行并因此是值,这并非巧合.

declare @testTable table 
(
    id int,
    Pgroup varchar(10),
    Pstatus varchar(3)
)
insert into @testTable select 3, 'Alpha', 'OK'
insert into @testTable select 3, 'Beta',  'NOK'
insert into @testTable select 3, 'Gamma', 'OK'
insert into @testTable select 3, 'Beta',  'OK'
insert into @testTable select 3, 'Beta',  'OK'

declare @Summary table
(
    id int,
    AlphaStatus varchar(3),
    BetaStatus varchar(3),
    GammaStatus varchar(3)
)
insert into @Summary (id) select 3

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'

select * from @summary
Run Code Online (Sandbox Code Playgroud)

我问的原因是,对于每个id,AlphaStatus,BetaStatus,GammaStatus如果之前是'NOK',则不应该改回'OK'.一旦它更新为'NOK',无论下一步是什么,它都会保持这种状态.

另一种方法是使用'OK'值更新@Summary,然后使用'NOK'进行另一次更新.这样我就知道'NOK'不会被取代.但如果这样可行,那么我宁愿使用它.

作为第二个问题,如果我使用INNER JOIN,为什么UPDATE不能正常工作?

谢谢.

Luk*_*zda 6

让我们看看是什么回报select而不是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'
Run Code Online (Sandbox Code Playgroud)

结果:

AlphaStatus     BetaStatus  GammaStatus
OK              NOK         OK
OK              OK          OK
OK              OK          OK
Run Code Online (Sandbox Code Playgroud)

现在你尝试做 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'
Run Code Online (Sandbox Code Playgroud)

更新表@Summary包含:

id  AlphaStatus     BetaStatus  GammaStatus
3   OK              NOK         OK
Run Code Online (Sandbox Code Playgroud)

我想你想得到:

id  AlphaStatus     BetaStatus  GammaStatus
3   OK              OK      OK
Run Code Online (Sandbox Code Playgroud)

但是UPDATE不起作用,当它们是多个匹配时,结果可能不一致,并且它部分地基于表排序或实际执行计划.

另请参阅:让我们弃用UPDATE FROM!

正确性?呸,谁在乎呢?

嗯,大部分都做.这就是我们测试的原因.

如果我搞乱了SELECT查询中的连接条件,以便第二个表中的太多行匹配,我会在测试时立即看到它,因为我会得到更多的行然后预期.如果我以类似的方式弄乱ANSI标准UPDATE查询中的子查询条件,我会更快地看到它,因为如果子查询返回的值超过单个值,SQL Server将返回错误.但是使用专有的UPDATE FROM语法,我可以搞砸连接并且永远不会注意到 - 如果SQL Server在连接表中匹配多行,那么SQL Server会很乐意一遍又一遍地更新同一行,只有最后一行的结果那些更新坚持下去.而且无法知道将会是哪一行,因为这取决于恰好选择的查询执行计划.最糟糕的情况是执行计划恰好在单处理器开发服务器的所有测试期间产生预期结果 - 然后,在部署到四路双核生产服务器之后,我们的宝贵数据突然击中了粉丝......

看到这种不一致而不是表变量使用表并创建聚簇索引:

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);  
Run Code Online (Sandbox Code Playgroud)

例如,如果您使用MERGE:

;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);
Run Code Online (Sandbox Code Playgroud)

您将收到明确的错误消息,指出这是不允许的:

MERGE语句尝试多次更新或删除同一行.当目标行与多个源行匹配时会发生这种情况.MERGE语句不能多次更新/删除目标表的同一行.优化ON子句以确保目标行最多匹配一个源行,或使用GROUP BY子句对源行进行分组.