【问题标题】:Does sp_executesql support multiple values in one parameter and return multiple records?sp_executesql 是否支持一个参数中的多个值并返回多条记录?
【发布时间】:2021-05-14 10:55:38
【问题描述】:

我创建了一个如下所示的存储过程,但它只返回一行而不是 3:

CREATE PROCEDURE [dbo].[tempsp] 
    (@RecycleIds NVARCHAR(MAX) = NULL)  
AS  
BEGIN  
    DECLARE @Err INT  
    DECLARE @WhereClause NVARCHAR(MAX)  
    DECLARE @SQLText1 NVARCHAR(MAX)  
    DECLARE @SQLText NVARCHAR(MAX)
  
    SET @SQLText1 = 'SELECT FROM dbo.SKU '  
  
    IF @RecycledSkuIds IS NOT NULL  
    BEGIN      
        SET @SQLText = 'SELECT FROM dbo.SKU WHERE SKU.SkuId IN (@RecycleIds)'

        EXEC sp_executesql @SQLText, N'@RecycleSkuIds nvarchar', @RecycleIds 
    END   
    ELSE  
    BEGIN  
        EXEC(@SQLText1)
    END           
  
    SET @Err = @@ERROR  
    RETURN @Err  
END  
-------end of stored procedure--------


EXEC tempsp @RecycleIds = '5,6,7'

运行这条SQL语句后,它只返回一行而不是3,id为5、6、7。

谁能告诉我我做错了什么? 我想使用 sp_executesql,这样它就可以安全地防止定义了强类型的 sql 注入。

【问题讨论】:

  • 如果你像这样定义你的参数:N'@RecycleSkuIds nvarchar', - 那么你得到一个长度为恰好一个字符的参数,这很可能不是你想要的。你真的应该养成在定义char(n)/nchar(n)/varchar(n)/nvarchar(n) 列、变量或参数时总是定义显式长度的习惯!

标签: sql sql-server sp-executesql


【解决方案1】:

使用表类型参数,带有强类型列:

CREATE TYPE dbo.IDs AS table (ID int);
GO

CREATE PROCEDURE [dbo].[tempsp] @RecycleIds dbo.IDs READONLY AS
BEGIN

    IF EXISTS (SELECT 1 FROM @RecycleIds)
        SELECT * --Replace with needed columns
        FROM dbo.SKU S
        --Using EXISTS in case someone silly puts in the same ID twice.
        WHERE EXISTS (SELECT 1
                      FROM @RecycleIds R
                      WHERE R.ID = S.SkuID);
    ELSE
        SELECT * --Replace with needed columns
        FROM dbo.SKU S
END;
GO

然后你可以像这样执行它:

EXEC dbo.tempsp; --All Rows
GO
DECLARE @RecycleIds dbo.IDs;
INSERT INTO @RecycleIds
VALUES(1),(40),(182);

EXEC dbo.tempsp @RecycleIds;

【讨论】:

  • 最好还添加如何使用sp_executesql 传递参数。此外,如果您的表类型有一个主键,那么您将不需要exists,它也可能表现更好
  • 我没有使用sp_executesql @Charlieface ...我为什么要解释如何使用它?这里实际上不需要动态 SQL。
  • 非常正确,但是 OP ,并且由于厨房水槽查询可能需要
  • 再一次,对于我们的问题@Charlieface,不需要动态 SQL。如果这是问题,那么 OP 应该发布一个新问题。虽然 OP 清楚地知道如何将参数传递给sp_executesql;他们只是没有正确定义长度。
【解决方案2】:

我试图在 IN 子句中检索 id 匹配的行。

SET @INClauseIds='''' + replace(@Ids, ',', ''',''') + ''''  

上面的语句会将 ID 的 ='1,2,3' 转换为 '1','2','3' 我可以直接放在 IN 子句中。

SET @SQLText1 ='EXEC(''SELECT  Name,SEOFriendlyName FROM SKU Where Id IN ( ''+ @Ids+'' ) )'             

 EXEC sp_executesql @SQLText1 ,N'@INClauseIds nvarchar(max)',@Ids=@INClauseIds 

如果您想避免使用会增加额外计算时间的临时表。您可以通过上述策略检索 n 条记录。安全,与 sp_executesql 强耦合,没有任何 sql 注入。

【讨论】:

  • 然而,这对注入开放,因此应该使用。
【解决方案3】:

您不能使用IN。或者,更准确地说,你有一个字符串,你把它和一个列表混淆了。一种方法是改用LIKE

SET @SQLText = '
SELECT *
FROM dbo.SKU
WHERE CONCAT('','', @RecycleIds, '','') LIKE CONCAT(''%,'', SKU.SkuId, '',%'')
';

【讨论】:

  • 不需要% LIKE 通配符?
  • @jarlh 。 . .谢谢。
  • 我会注意到这个解决方案不会是 SARGable,所以对于大型表来说可能远非高性能。 SkuID 列上的索引将无法使用,如果该列是 CLUSTERED INDEX,考虑到该表称为 Sku,我不会感到惊讶。
猜你喜欢
  • 2013-08-31
  • 1970-01-01
  • 1970-01-01
  • 2015-07-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-12
相关资源
最近更新 更多