3

假设您正在对一个表运行 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
4

3 回答 3

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

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

于 2009-02-06T15:58:50.983 回答
5

从 BOL 更新

将 UPDATE 与 FROM 子句一起使用

如果 UPDATE 语句包含未指定的 FROM 子句,则该语句的结果是未定义的,即,如果 UPDATE 语句不是确定性的,则每个更新的列出现只有一个值可用。例如,在下面脚本的 UPDATE 语句中,Table1 中的两行都满足 UPDATE 语句中 FROM 子句的条件;但未定义 Table1 中的哪一行用于更新 Table2 中的行。

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 子句中的第一条或最后一条记录,因为它未定义。

于 2009-02-06T16:00:22.477 回答
-1

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

我很确定如果试图做一些像这样模棱两可的事情,Firebird 会抛出异常。但是Firebird根本不支持(非标准?)sntax update X from X join Y ...

于 2009-10-05T14:03:41.047 回答