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

时间:2015-09-03 21:24:30

标签: sql tsql

此脚本中的结果是正确的,但我似乎不明白为什么列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

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

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

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

感谢。

1 个答案:

答案 0 :(得分: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'

结果:

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

现在您尝试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'

更新后的表@Summary包含:

id  AlphaStatus     BetaStatus  GammaStatus
3   OK              NOK         OK

我想你想得到:

id  AlphaStatus     BetaStatus  GammaStatus
3   OK              OK      OK

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

另请参阅:Let's deprecate UPDATE FROM!

  

<强>正确性?巴,谁在乎?

     

嗯,大多数人都这么做。这就是我们测试的原因。

     

如果我在SELECT查询中弄乱了连接条件,那么行太多了   从第二场比赛开始,我会在考试时看到它,因为我   获得更多的行然后预期。如果我弄乱了子查询标准   在ANSI标准UPDATE查询中,我以类似的方式查看它   越早,因为如果子查询SQL Server将返回错误   返回多个值。但使用专有的UPDATE FROM   语法,我可以弄乱连接,从不注意 - 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);  

例如,如果您使用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);

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

  

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