SQL Server说:'多部分标识符“shinola.a”无法绑定。'
我在这里做错了什么?
declare @foo table (
a int,
b int
);
insert into @foo values ( 1, 2 ), ( 3, 4 );
declare @xml XML = '<shinola><a>1</a><b>5</b></shinola>';
-- OK, now this is where it breaks:
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
set
b = shinola.b
where
a = shinola.a
(我知道还有其他方法可以做到这一点,我只是在我正在编写的代码中检查了其中一个。我想了解我对这种方法的理解并不了解。)
答案 0 :(得分:1)
1)错误消息是由shinola.a
表达式(where
a = shinola.a
)引起的,SQL Server在from
语句的update
子句或{{@foo
子句中找不到该表达式1}}表。如您所见,此时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;