【问题标题】:SQL Server doesn't allow to execute subquery as dynamic SQLSQL Server 不允许将子查询作为动态 SQL 执行
【发布时间】:2012-04-01 00:32:30
【问题描述】:

我尝试创建一个动态 SQL 来获取某些用户的所有任务

过程 [GetAllSubExecutorsByUserId] 返回当前用户的所有子节点的 ID 我将这些 ID 写入一个临时表,然后我想创建一个动态 SQL 来从 [tasks] 表中获取所有任务,其中“Executor”列的值在这个临时表中

我写的查询如下:

DECLARE @UserId VARCHAR(10) = 72;

DECLARE @tmp TABLE  ( Id VARCHAR(10));
INSERT @tmp exec [dbo].[GetAllSubExecutorsByUserId] @Source = @UserId;

DECLARE @SQL VARCHAR(max);

SELECT @SQL = 'SELECT * FROM tasks ';
SELECT @SQL = @SQL + 'WHERE Executor IN (' + (select Id from @tmp) + ')';

EXEC(@SQL);

但是当我运行它时,它给出了一个错误:

子查询返回超过 1 个值。当子查询跟随 =、!=、、>= 或子查询用作表达式时,这是不允许的。

而且我不明白如何修复它,因为如果我运行相同的查询(这不是动态 SQL,它可以完美运行)

有效的查询是静态的:

DECLARE @UserId VARCHAR(10) = 72;

DECLARE @tmp TABLE  ( Id VARCHAR(10));
INSERT @tmp exec [dbo].[GetAllSubExecutorsByUserId] @Source = @UserId;

SELECT * FROM tasks WHERE Executor IN (select Id from @tmp)

但我需要一个动态的 SQL... 请帮我解决这个问题。

谢谢。

【问题讨论】:

    标签: sql dynamic subquery temp-tables


    【解决方案1】:

    这是因为您试图从一个返回多个值的选择中设置一个字符串值。 SQL 不知道如何将此列表转换为逗号分隔的列表。在准备好的语句中,它作为真正的IN 针对返回的“表”运行,因此它可以针对该子查询表运行 SET 运算符。但是,在动态查询中,您实际上是在尝试创建类似的内容:

    ...IN (1,2,3)
    

    你必须像下面这样重写动态查询,让它像准备好的语句一样运行:

    'WHERE Executor IN (select Id from @tmp)'
    

    如果你这样做,那么你将不得不将表变量传递给 exec,但你必须使用sp_executesql。因此,您的代码变为:

    DECLARE @UserId VARCHAR(10) = 72;
    
    DECLARE @tmp TABLE  ( Id VARCHAR(10));
    INSERT @tmp exec [dbo].[GetAllSubExecutorsByUserId] @Source = @UserId;
    
    DECLARE @SQL VARCHAR(max);
    
    SELECT @SQL = 'SELECT * FROM tasks ';
    SELECT @SQL = @SQL + 'WHERE Executor IN (select Id from @tmp)';
    
    DECLARE @SQLParamSetup VARCHAR(150);
    SET @SQLParamSetup = '@tmp TABLE  ( Id VARCHAR(10))'
    
    
    EXEC sp_executesql @SQL, @SQLParamSetup, @tmp ;
    

    但是,另一个更简洁的选择是使用临时表(不是变量),因为它会在您的整个连接中保持活动状态(即使在子查询中)

    DECLARE @UserId VARCHAR(10) = 72;
    
    CREATE TABLE #tmp  ( Id VARCHAR(10));
    INSERT INTO #tmp exec [dbo].[GetAllSubExecutorsByUserId] @Source = @UserId;
    
    DECLARE @SQL VARCHAR(max);
    
    SELECT @SQL = 'SELECT * FROM tasks ';
    SELECT @SQL = @SQL + 'WHERE Executor IN (select Id from #tmp)';
    
    EXEC @SQL ;
    

    【讨论】:

    • 据我了解,我应该将“从@tmp 中选择 Id”子查询的结果作为 CSV 字符串...但是 sp_executesql 呢?>
    • 我不知道你从哪里得到这样的理解,但子查询将转换为 CSV 是不正确的。我将更新我的答案,以说明如何将其用于 sp_executesql,如所述
    • 贾斯汀,在这一行: SET @SQLParamSetup = "'@tmp TABLE (Id VARCHAR(10))' 出了点问题......如果我删除那些双引号 (") 就会出现错误消息“sp_executesql 附近的 sintax 不正确”你能建议我如何适应...谢谢
    • 对不起,您必须执行执行 sql 的存储过程:p。我已经更新了代码
    • 贾斯汀,你能解释一下,为什么使用 EXEC sp_executesql @SQL, @SQLParamSetup, @tmp ; 它也不执行.. 我创建了 # Executors 表 作为临时表,我已将 @SQL 查询更改为 NVARCHAR(因为 sp_executesql 只需要 NVARCHAR、NTEXT 和 NCHAR)DECLARE @SQL NVARCHAR(max) SET @SQL = 'SELECT * FROM tasks ' SET @SQL = @SQL + ' WHERE tasks.Executor IN (SELECT Id FROM #Executors)' DECLARE @ParamDef NVARCHAR(1000) SET @ParamDef = '#Executors TABLE (Id NVARCHAR(10))' Execute sp_executesql @SQL, @ParamDef, #Executors 它给出_“#Executors”附近的语法不正确。
    【解决方案2】:

    使用sp_executesql可以传入参数。

    【讨论】:

    • 嗯,你所拥有的真的很简单,不需要动态 sql,所以我猜你有一个更复杂的查询,但你可以将表变量作为参数传递给动态sql
    【解决方案3】:
    declare @ddto as char(10)='2020/10/01',
    set @month= (select month(@ddto))
    set @year =(select year(@ddto))
    
    you can change in the dynamic query as following:
    select column1 where
    Material_Issued_to_div='''+ @div+''' and 
    month(indent_date)='''+@month+'''       and 
    year(indent_date)='''+@year+''' 
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-20
      • 2017-01-11
      • 1970-01-01
      相关资源
      最近更新 更多