使用一对多 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
运行 上面的内容而不是更新,它看起来和我想的一模一样,我认为它意味着“四个”更新,同时填充 value1
和 value2
在两行中。
如果有人能向我解释一下,我将不胜感激。
修改了更新子句,在获取 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。
我 运行 遇到了一个问题,我不确定这是否是预期的行为。我在网上搜索了一个答案,但我能找到的只是最后更新哪一行以及查询后的值是多少。我知道在那种情况下没有订单这样的东西,你无法提前确定价值是多少。
但就我而言,我正在更新不同的列,因此覆盖之前的更新与我无关。
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
运行 上面的内容而不是更新,它看起来和我想的一模一样,我认为它意味着“四个”更新,同时填充 value1
和 value2
在两行中。
如果有人能向我解释一下,我将不胜感激。
修改了更新子句,在获取 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 anUPDATE
statement are undefined if the statement includes aFROM
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 theUPDATE
statement is not deterministic. For example, in theUPDATE
statement in the following script, both rows in Table1 meet the qualifications of theFROM
clause in theUPDATE
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。