使用一对多 JOIN 更新(多列)

UPDATE with a one-to-many JOIN (multiple columns)

我 运行 遇到了一个问题,我不确定这是否是预期的行为。我在网上搜索了一个答案,但我能找到的只是最后更新哪一行以及查询后的值是多少。我知道在那种情况下没有订单这样的东西,你无法提前确定价值是多少。

但就我而言,我正在更新不同的列,因此覆盖之前的更新与我无关。

CREATE TABLE #original (id int, value1 int, value2 int)
INSERT INTO #original (id) VALUES (1), (2)

CREATE TABLE #temp (id int, name varchar(10), value int)
INSERT INTO #temp (id, name, value) VALUES (1, 'value1', 10), (1, 'value2', 11), (2, 'value1', 20), (2, 'value2', 21)

SELECT * FROM #original

id          value1      value2
----------- ----------- -----------
1           NULL        NULL
2           NULL        NULL

SELECT * FROM #temp

id          name       value
----------- ---------- -----------
1           value1     10
1           value2     11
2           value1     20
2           value2     21

UPDATE O SET 
    value1 = CASE WHEN T.name = 'value1' THEN T.value ELSE value1 END,
    value2 = CASE WHEN T.name = 'value2' THEN T.value ELSE value2 END
FROM
    #original O
        INNER JOIN #temp T ON T.id = O.id

SELECT * FROM #original

id          value1      value2
----------- ----------- -----------
1           10          NULL
2           20          NULL

我不明白为什么 value2 都是 NULL

SELECT
    O.id,
    CASE WHEN T.name = 'value1' THEN T.value ELSE value1 END AS value1,
    CASE WHEN T.name = 'value2' THEN T.value ELSE value2 END AS value2
FROM
    #original O
        INNER JOIN #temp T ON T.id = O.id

id          value1      value2
----------- ----------- -----------
1           10          NULL
1           NULL        11
2           20          NULL
2           NULL        21

运行 上面的内容而不是更新,它看起来和我想的一模一样,我认为它意味着“四个”更新,同时填充 value1value2在两行中。

如果有人能向我解释一下,我将不胜感激。

修改了更新子句,在获取 value1 和 value2 时做了一些调整

;with cte as (SELECT
    O.id,
    max(CASE WHEN T.name = 'value1' THEN T.value ELSE value1 END) AS value1,
    max(CASE WHEN T.name = 'value2' THEN T.value ELSE value2 END) AS value2

FROM
    #original O
        INNER JOIN #temp T ON T.id = O.id
        group by o.id
        )
UPDATE o SET 
    o.value1 = i.value1 ,
    o.value2 = i.value2 

FROM
    #original o
        INNER JOIN cte  i ON i.id = O.id

这里的问题是你的假设:“但在我的例子中,我正在更新不同的列,所以我不关心覆盖以前的更新。” 它肯定一个问题,因为最终,你的代码

value1 = CASE WHEN T.name = 'value1' THEN T.value ELSE value1 END,
value2 = CASE WHEN T.name = 'value2' THEN T.value ELSE value2 END

每行只运行(或生效)一次。 SQL 服务器不需要更新同一行两次,通常会任意取一行的值,其中之一是 NULL.

The documentation states (我加粗):

Use caution when specifying the FROM clause to provide the criteria for the update operation. The results of an UPDATE statement are undefined if the statement includes a FROM clause that is not specified in such a way that only one value is available for each column occurrence that is updated, that is if the UPDATE statement is not deterministic. For example, in the UPDATE statement in the following script, both rows in Table1 meet the qualifications of the FROM clause in the UPDATE statement; but it is undefined which row from Table1 is used to update the row in Table2.

只能使用一行并完成一次更新,您不能假设更新会按顺序发生。因此,您需要确保要更新的每一行都有 单个 匹配项。

因此你应该pre-aggregate你的价值观

UPDATE O SET 
    value1 = T.value1,
    value2 = T.value2
FROM
    #original O
INNER JOIN (
    SELECT
      id,
      MAX(CASE WHEN T.name = 'value1' THEN T.value END) value1,
      MAX(CASE WHEN T.name = 'value2' THEN T.value END) value2,
    FROM #temp T
    GROUP BY
      id
) T ON T.id = O.id;

您也可以为此使用 CROSS APPLY 或 CTE。