Sql server 为什么递归字符串连接中的子查询总是返回NULL?

Sql server 为什么递归字符串连接中的子查询总是返回NULL?,sql-server,string,sql-server-2005,Sql Server,String,Sql Server 2005,假设我有两张桌子: 表1:有一个我需要处理的ID列表。 表2:具有ID和值的键值对。 我需要从表2中检索表1中所有ID的值,作为单个字符串。为了实现这一点,我进行了下一个查询: DECLARE @id INT, @value VARCHAR(10); SELECT @id=0, @value=''; SELECT @value = @value + (SELECT TOP 1 value FROM TABLE2 WHERE id=@id) + '-', @id = @id+1

假设我有两张桌子:

表1:有一个我需要处理的ID列表。 表2:具有ID和值的键值对。 我需要从表2中检索表1中所有ID的值,作为单个字符串。为了实现这一点,我进行了下一个查询:

DECLARE @id INT, @value VARCHAR(10);

SELECT @id=0, @value='';

SELECT
    @value = @value + (SELECT TOP 1 value FROM TABLE2 WHERE id=@id) + '-',
    @id = @id+1
FROM TABLE1

但是当我运行这个查询时,子查询总是返回null,因此,在末尾@value=null。我的问题是:为什么子查询从表2中选择TOP 1值,其中id=@id总是返回NULL,即使在表2中找到id也是如此?

这是一种非常奇怪的尝试实现treewalker的方法,在SELECT中增加@id并期望它应用于子查询。这不起作用,因为SQL Server在查询设置阶段将子查询表达式的@id值锁定为常量

请参见此示例,其中返回的@value明确表示@id锁定在1。对于您的问题,它被锁定为0,因此每个子查询将返回NULL,表面上是因为@id=0不匹配

create table table1 (
  id int);
create table table2 (
  id int, value varchar(10));
insert table1 values (1),(2),(3),(4);
insert table2 values
(1,1),
(2,2),
(3,3),
(4,4);

DECLARE @id INT, @value VARCHAR(10);

SELECT @id=1, @value='';

SELECT
    @value = @value + (SELECT TOP 1 value FROM TABLE2 WHERE id=@id) + '-',
    @id = @id+1
FROM TABLE1;

select @value, @id

-- result
1-1-1-1       5
如果只需要2中的值,则不需要变量@id,只需将子查询与table.id关联,如下所示:

create table table1 (id int);
create table table2 (id int, value varchar(10));
insert table1 values (1),(2),(3),(4);
insert table2 values
(1,1),
(3,9),
(4,4);

DECLARE @value VARCHAR(10);

SELECT @value='';

SELECT
    @value = @value + isnull((SELECT TOP 1 value 
                              FROM TABLE2 
                              WHERE id=table1.id) + '-','')
FROM TABLE1;

select @value

-- Result
1-9-4

这是一种非常奇怪的尝试实现treewalker的方法,在SELECT中增加@id并期望它应用于子查询。这不起作用,因为SQL Server在查询设置阶段将子查询表达式的@id值锁定为常量

请参见此示例,其中返回的@value明确表示@id锁定在1。对于您的问题,它被锁定为0,因此每个子查询将返回NULL,表面上是因为@id=0不匹配

create table table1 (
  id int);
create table table2 (
  id int, value varchar(10));
insert table1 values (1),(2),(3),(4);
insert table2 values
(1,1),
(2,2),
(3,3),
(4,4);

DECLARE @id INT, @value VARCHAR(10);

SELECT @id=1, @value='';

SELECT
    @value = @value + (SELECT TOP 1 value FROM TABLE2 WHERE id=@id) + '-',
    @id = @id+1
FROM TABLE1;

select @value, @id

-- result
1-1-1-1       5
如果只需要2中的值,则不需要变量@id,只需将子查询与table.id关联,如下所示:

create table table1 (id int);
create table table2 (id int, value varchar(10));
insert table1 values (1),(2),(3),(4);
insert table2 values
(1,1),
(3,9),
(4,4);

DECLARE @value VARCHAR(10);

SELECT @value='';

SELECT
    @value = @value + isnull((SELECT TOP 1 value 
                              FROM TABLE2 
                              WHERE id=table1.id) + '-','')
FROM TABLE1;

select @value

-- Result
1-9-4

另一种解决方案是,如果表2中的值不为空,则此解决方案仅在表2中的值之间附加“-”分隔符:

declare @id int, @value varchar(max);
select @id = 0, @value='';

select 
    @value = @value + isnull(value,'') + case when value is null then '' else '-' end
from 
    table2 t2
where 
    t2.id in (select id from table1)

另一种解决方案是,如果表2中的值不为空,则此解决方案仅在表2中的值之间附加“-”分隔符:

declare @id int, @value varchar(max);
select @id = 0, @value='';

select 
    @value = @value + isnull(value,'') + case when value is null then '' else '-' end
from 
    table2 t2
where 
    t2.id in (select id from table1)

这是因为您的@id从0开始。除非表中的id为0,否则可能需要执行以下操作:选择@id=1

或者,您可以将其压缩为此,而不使用id:

SELECT @value = @value + value +  '-'
FROM TABLE2 t2
INNER JOIN TABLE1 t1 ON t1.id = t2.id

这是因为您的@id从0开始。除非表中的id为0,否则可能需要执行以下操作:选择@id=1

或者,您可以将其压缩为此,而不使用id:

SELECT @value = @value + value +  '-'
FROM TABLE2 t2
INNER JOIN TABLE1 t1 ON t1.id = t2.id

是的,但我很好奇为什么子查询总是返回NULL。因为它做的第一件事是从表2中选择TOP 1值,其中id=0的计算结果为NULL,NULL+'value'=NULL。我想我必须放弃这种方式,而是使用while循环。为了将来的参考,我可以用CTE代替这种方法吗?我认为你不需要while循环或CTE。还有两个答案可以满足您的需求。除非你需要对值或类似的东西进行分组,你没有提到。@Giscard Biamby,这只是一个简短的例子来说明我的问题,但我实际做的比这个复杂得多。我不能使用其他示例,因为我需要每个循环的最后一个ID,而不是当前ID。是的,但我想知道为什么子查询总是返回NULL。因为它做的第一件事是从表2中选择前1个值,其中ID=0的计算结果为NULL,NULL+'value'=NULL。我想我必须放弃这种方式,使用while循环代替。为了将来的参考,我可以用CTE代替这种方法吗?我认为你不需要while循环或CTE。还有两个答案可以满足您的需求。除非你需要对值或类似的东西进行分组,你没有提到。@Giscard Biamby,这只是一个简短的例子来说明我的问题,但我实际做的比这个复杂得多。我不能使用其他示例,因为我需要每个循环的最后一个ID,而不是当前ID。