这个模糊的UPDATE语句为什么/如何工作?

时间:2009-02-06 15:42:59

标签: sql database sql-server-2005 theory

假设您在表上运行UPDATE语句,但是您在此基表中放入的信息来自其他一些辅助表。通常,您将加入数据,而不是期望UPDATE语句的FROM子句中的行相乘,从而保持一个新行映射到基表中的一个旧行。

但是我想知道如果你的JOIN表在某种程度上不明确会发生什么,就像你无法将每个基本实体映射到一个连接的实体一样。或者,如果你做了一些荒谬的事情,比如将基表连接到其子表,并使用该信息更新基表。它会如何选择?现在每个基表行有多行。

我在SQL Server 2005中运行了这样的语句,它似乎是在每个集合中选择第一行。但这对我来说似乎不对。不应该发错吗?为什么这是理想的行为?

示例代码

-- normal
-- categories are one-to-many bundles

update bundles_denormalized set category = c.description

from bundles_denormalized b
left join categories c
on b.category_id = c.id

-- ambiguous
-- bundles are one-to-many products

update bundles_denormalized set category = p.description

from bundles_denormalized b
left join products p
on b.id = p.bundle_id

3 个答案:

答案 0 :(得分:5)

实际上,如果我正确地理解了这个问题,那就是多次更新字段,只是因为只有一条记录,所以它最终只有一个值。为什么不出错?因为语法是正确的,并且数据库无法知道您的意图是什么。你想这样做吗?通常情况下,这就是为什么您应该在运行之前选择更新以确保更正记录获得正确的值。

我通常用这种方式写一个更新:

update b    
set category = p.description
--select b.category, p.description
from bundles_denormalized b
left join products p on b.id = p.bundle_id

我也会担心在更新中使用左连接,因为您可能会将值更改为空值。如果这是你想要的,那就没关系,但如果不是,那就没关系。

答案 1 :(得分:4)

From BOL on UPDATE

  

将UPDATE与FROM子句一起使用

     

UPDATE语句的结果是   如果声明包含a,则为undefined   未在中指定的FROM子句   只有一个价值的方式   可用于每个列的出现   更新,即如果更新   陈述不确定。   例如,在UPDATE语句中   在以下脚本中,两行都在   表1符合资格   UPDATE语句中的FROM子句;   但它未定义哪一行   Table1用于更新行   表2。

USE AdventureWorks;
GO
IF OBJECT_ID ('dbo.Table1', 'U') IS NOT NULL
    DROP TABLE dbo.Table1;
GO
IF OBJECT_ID ('dbo.Table2', 'U') IS NOT NULL
    DROP TABLE dbo.Table2;
GO
CREATE TABLE dbo.Table1 
    (ColA int NOT NULL, ColB decimal(10,3) NOT NULL);
GO
CREATE TABLE dbo.Table2 
    (ColA int PRIMARY KEY NOT NULL, ColB decimal(10,3) NOT NULL);
GO
INSERT INTO dbo.Table1 VALUES(1, 10.0);
INSERT INTO dbo.Table1 VALUES(1, 20.0);
INSERT INTO dbo.Table2 VALUES(1, 0.0);
GO
UPDATE dbo.Table2 
SET dbo.Table2.ColB = dbo.Table2.ColB + dbo.Table1.ColB
FROM dbo.Table2 
    INNER JOIN dbo.Table1 
    ON (dbo.Table2.ColA = dbo.Table1.ColA);
GO
SELECT ColA, ColB 
FROM dbo.Table2;

换句话说,它是一个有效的语法,它不会抛出错误或异常。

但与此同时,您无法确定更新值是否是FROM子句中的第一个或最后一个记录,因为它未定义。

答案 2 :(得分:-1)

我实际上只是注意到它在我的案例中做了一些非常愚蠢的事情。我的意图是创建一个单行选择,但显然有重复,我没想到。在这种情况下,它实际上将数据混合输入到目标行,从第一个源行中选择一些列,从第二个源行中选择一些列。

我很确定Firebird会抛出一个例外,如果试图做一些像这样的诡计。但是Firebird不支持X加入Y的(非标准?)sntax更新X ...