You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

存储过程返回字符串作为IN查询参数无效问题排查

问题:存储过程返回的字符串在IN子句中无法生效的原因及解决方法

存储过程及问题复现

我编写了如下存储过程:

ALTER PROCEDURE [dbo].[GetPhysicalChildNodesAsString]
    -- 存储过程参数
    @Book varchar(50),
    @Vertical varchar(50)
AS
BEGIN
    DECLARE @results nvarchar(max)
;WITH cte AS 
 (
  SELECT a.BookID, a.ParentBookID, a.BookName
  FROM DimBook a
  WHERE BookName = @Book
  UNION ALL
  SELECT a.BookID, a.ParentBookID, a.BookName
  FROM DimBook a JOIN cte c ON a.ParentBookID = c.BookID
  and HighPortfolioID = ( select PortfolioID from portfolioMaster where PortfolioName = @Vertical and PortfolioType='HighPortfolio')
  )
  select @results = coalesce(@results + ',', '')+'''' +  convert(varchar(max),BookName)+''''
  from cte Where Len(BookName) < 20

  select @results as Book

END

该存储过程返回的字符串格式如下:

'Physical','Anish Vohra','Sandeep Bajoria','Nirav Desai','Sushil Mohta','Sahil Pasad','G R Poddar','Direct','Sales Broker','Internal Transfer','Sprint','Murji Meghan','Intra Oils & Fats','GGN','GGN1','GGN2','Book1','Book2','Book 3','Book4','Book5','Comglobal','Sunvin','PVOC','Afro Asian'

无效的查询方式

执行以下查询时,仅返回表表头,没有数据:

DECLARE @BooksList varchar(max) 
DECLARE @t table(BookNames varchar(max) )
   INSERT @t(BookNames)
   EXEC @BooksList = GetPhysicalChildNodesAsString 'Physical','Enterprise'
   
   SELECT @BooksList = BookNames FROM @t

   select @BooksList as 'books'

   select * from DimBook where BookName IN(select @BooksList as 'books')

有效的查询方式

直接将上述字符串传入IN子句时,查询结果正常:

select * from DimBook where BookName IN('Physical','Anish Vohra','Sandeep Bajoria','Nirav Desai','Sushil Mohta','Sahil Pasad','G R Poddar','Direct','Sales Broker','Internal Transfer','Sprint','Murji Meghan','Intra Oils & Fats','GGN','GGN1','GGN2','Book1','Book2','Book 3','Book4','Book5','Comglobal','Sunvin','PVOC','Afro Asian')

原因分析

IN(select @BooksList as 'books')的执行逻辑是:把变量@BooksList的整个字符串当成单个值,去匹配DimBook表中的BookName字段。而表里不存在任何一条BookName等于这个完整长字符串的记录,所以自然返回空结果。

直接写字符串时,数据库会把逗号分隔的每个单引号包裹内容识别为独立的匹配值,所以能正确查询。

解决方法

方法1:修改存储过程,返回多行结果而非拼接字符串

这是最稳妥的方案,直接让存储过程返回符合条件的所有BookName,调用时直接用IN子句关联:

ALTER PROCEDURE [dbo].[GetPhysicalChildNodes]
    @Book varchar(50),
    @Vertical varchar(50)
AS
BEGIN
;WITH cte AS 
 (
  SELECT a.BookID, a.ParentBookID, a.BookName
  FROM DimBook a
  WHERE BookName = @Book
  UNION ALL
  SELECT a.BookID, a.ParentBookID, a.BookName
  FROM DimBook a JOIN cte c ON a.ParentBookID = c.BookID
  and HighPortfolioID = ( select PortfolioID from portfolioMaster where PortfolioName = @Vertical and PortfolioType='HighPortfolio')
  )
  SELECT BookName
  FROM cte 
  WHERE Len(BookName) < 20
END

调用示例:

-- 直接用EXEC作为IN子句的数据源
SELECT * 
FROM DimBook 
WHERE BookName IN(EXEC GetPhysicalChildNodes 'Physical','Enterprise')

-- 或者用临时表接收后查询
DECLARE @t table(BookNames varchar(max))
INSERT INTO @t
EXEC GetPhysicalChildNodes 'Physical','Enterprise'

SELECT * 
FROM DimBook 
WHERE BookName IN(SELECT BookNames FROM @t)

方法2:使用动态SQL执行查询

如果必须保留原存储过程的返回格式,可以用动态SQL把字符串拼接到查询语句中执行:

DECLARE @BooksList varchar(max) 
DECLARE @t table(BookNames varchar(max))
INSERT INTO @t(BookNames)
EXEC GetPhysicalChildNodesAsString 'Physical','Enterprise'

SELECT @BooksList = BookNames FROM @t

DECLARE @sql nvarchar(max)
SET @sql = N'SELECT * FROM DimBook WHERE BookName IN(' + @BooksList + N')'
EXEC sp_executesql @sql

⚠️ 注意:使用动态SQL要警惕SQL注入风险,如果@Book或@Vertical参数来自用户输入,必须确保参数经过安全校验。

方法3:用字符串拆分函数(SQL Server 2016+)

如果你的SQL Server版本是2016及以上,可以用STRING_SPLIT函数拆分字符串后查询:

DECLARE @BooksList varchar(max) 
DECLARE @t table(BookNames varchar(max))
INSERT INTO @t(BookNames)
EXEC GetPhysicalChildNodesAsString 'Physical','Enterprise'

SELECT @BooksList = BookNames FROM @t
-- 清理字符串中的单引号,转换为适合拆分的格式
SET @BooksList = REPLACE(@BooksList, '''', '')

SELECT * 
FROM DimBook 
WHERE BookName IN(SELECT value FROM STRING_SPLIT(@BooksList, ','))

这种方法需要处理字符串格式,不如前两种可靠,仅适合临时场景。


内容的提问来源于stack exchange,提问作者venkat

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 02:09:09