0

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

(我知道还有其他方法可以做到这一点,我只是在我正在编写的代码中签入其中一种。我想了解我对这种方式的不理解。)

4

1 回答 1

1

1) 错误信息是由 SQL Server 在语句子句或表中找不到的shinola.a表达式 ( ) 引起的。如您所见,此时,公用表表达式未被语句引用。where a = shinola.afromupdate@fooshinolaupdate

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;
于 2013-09-19T18:55:25.617 回答