1) 错误信息是由 SQL Server 在语句子句或表中找不到的shinola.a
表达式 ( ) 引起的。如您所见,此时,公用表表达式未被语句引用。where
a = shinola.a
from
update
@foo
shinola
update
2)如果您想@foo
使用来自shinola
公用表表达式的数据更新表变量,那么您可以UPDATE ... FROM ...
这样使用:
...
with shinola ( a, b ) as (
...
)
update @foo
set b = shinola.b
from @foo as [target]
inner join shinola on [target].a = shinola.a;
select * from @foo;
结果:
a b
----------- -----------
1 5 <-- updated row
3 4
3) 这update
是不安全的,因为目标表 ( @foo
) 和源表 ( ) 之间的“关系”shinola
不是 1-1、1-0 而是 1-n(例如)。
示例:如果您因此更改 @xml 变量 (1-55, 1-5):
declare @xml XML = '<shinola><a>1</a><b>55</b></shinola>
<shinola><a>1</a><b>5</b></shinola>';
那么结果将是:
a b
----------- -----------
1 55 <-- row a=1 is updated with `55` instead of `5` (SQL Server choose a single value from the source. In this case the selected value was `55` instead of `5`).
3 4
在这种情况下,更安全的update
语句版本可能是:
...
with shinola ( a, b ) as (
...
)
update @foo
set b = (select shinola.b from shinola where [target].a = shinola.a)
--or better to avoid updating with NULLs
--set b = ISNULL( (select shinola.b from shinola where [target].a = shinola.a) , b )
from @foo as [target]
因为在这种情况下会引发错误:
Msg 512, Level 16, State 1, Line 11
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
The statement has been terminated.
4)更好的是,在这种情况下,当目标和源之间存在一对多“关系”时,您应该决定该怎么做。例如,您可以选择最小值 ( 5
)、最大值 ( 55
),也可以选择平均值 ( 30
):
declare @foo table (
a int,
b int
);
insert into @foo values ( 1, 2 ), ( 3, 4 );
declare @xml XML = '<shinola><a>1</a><b>55</b></shinola>
<shinola><a>1</a><b>5</b></shinola>';
with shinola ( a, b ) as (
select sh.value('a[1]', 'int') as a, sh.value('b[1]', 'int') as b
from @xml.nodes('/shinola') as doc(sh)
)
update @foo
-- if there are many value in the source (`shinola`) it finds the maximum value
set b = ISNULL( (select max(shinola.b) from shinola where [target].a = shinola.a) , b )
from @foo as [target];
select * from @foo;
结果:
a b
----------- -----------
1 55
3 4
5) 对于 SQL Server 2008+,您可以这样使用 MERGE:
declare @foo table (
a int,
b int
);
insert into @foo values ( 1, 2 ), ( 3, 4 );
declare @xml XML = '<shinola><a>1</a><b>55</b></shinola>
<shinola><a>1</a><b>5</b></shinola>';
with base ( a, b ) as (
select sh.value('a[1]', 'int') as a, sh.value('b[1]', 'int') as b
from @xml.nodes('/shinola') as doc(sh)
), shinola ( a, max_b ) as (
select a, MAX(b)
from base
group by a
)
merge into @foo as [target]
using shinola on [target].a = shinola.a
when matched then
update set b = shinola.max_b;
select * from @foo;