【问题标题】:How to parse a VARCHAR passed to a stored procedure in SQL Server?如何解析传递给 SQL Server 中存储过程的 VARCHAR?
【发布时间】:2015-06-13 22:21:23
【问题描述】:

我有两个表tbl_Productstbl_Brands,都加入了BrandId

我有一个存储过程,它应该返回所有属于作为参数传递给它的品牌 ID 的产品。

我的代码如下。

create proc Sp_ReturnPrdoucts
    @BrandIds varchar(500) = '6,7,8'
AS
BEGIN
    SELECT * 
    FROM tbl_Products as p 
    JOIN tbl_Brands b ON p.ProductBrandId = b.BrandId 
    WHERE b.BrandId IN (@BrandIds)
END

但这会产生错误,因为 BrandIdINT@BrandIdsVARCHAR

当我按如下方式对其进行硬编码时,它可以正常工作并从 db 中返回所需的数据 ..

create proc Sp_ReturnPrdoucts
    @BrandIds varchar(500) = '6,7,8'
AS
BEGIN
    SELECT * 
    FROM tbl_Products AS p 
    JOIN tbl_Brands b ON p.ProductBrandId = b.BrandId 
    WHERE b.BrandId IN (6,7,8)
END

任何帮助:)

【问题讨论】:

  • 复制:stackoverflow.com/questions/15585632/… 你只需要创建函数来将你的列表转换成表格,然后就可以内联了。
  • @EvaldasBuinauskas 在 sql 中拆分字符串通常不是解决方案,而是一种解决方法。正确的解决方案是尽可能使用正确的数据类型(在本例中为表)。
  • @ZoharPeled 您仍然需要以某种方式将这些值放在那里。
  • 旁注:您应该为您的存储过程使用sp_ 前缀。微软有reserved that prefix for its own use (see Naming Stored Procedures),你确实会在未来某个时候冒着名称冲突的风险。 It's also bad for your stored procedure performance。最好只是简单地避免 sp_ 并使用其他东西作为前缀 - 或者根本不使用前缀!
  • @EvaldasBuinauskas:你的意思是使用表值参数不能有默认值?

标签: sql sql-server sql-server-2008


【解决方案1】:

如果可能,不要对这类事情使用 varchar,而是使用 table valued parameter

要使用表值参数,您应该首先声明一个用户定义的表类型:

CREATE TYPE IntList As Table
(
    IntValue int
)

然后更改您的存储过程以接受此变量而不是 nvarchar:

create proc Sp_ReturnPrdoucts
    @BrandIds dbo.IntList readonly -- Note: readonly is a must!
AS
BEGIN

SELECT * 
FROM tbl_Products as p 
join tbl_Brands b on p.ProductBrandId=b.BrandId 
join @BrandIds ON(b.BrandId = IntValue)

END

问题是IN() 运算符需要一个由逗号分隔的变量列表,而您提供一个变量,它的值是一个逗号分隔的字符串。

如果不能使用表值参数,可以使用 sql 中的字符串拆分函数将 varchar 的值转换为 ints 表。那里有很多分离器,我建议在选择一个之前阅读this article

【讨论】:

  • 很好的固溶体!
【解决方案2】:

另一种选择是使用“间接”(我一直这么称呼它)

你可以这样做..

create proc Sp_ReturnPrdoucts
@BrandIds varchar(500) = '6,7,8'
AS
BEGIN
    if (isnumeric(replace(@BrandIds,',',''))=1) 
    begin
        exec('SELECT * FROM tbl_Products as p join tbl_Brands b on p.ProductBrandId=b.BrandId WHERE b.BrandId IN ('+@BrandIds+')')
    end
END

这样选择语句被构建为字符串,然后执行。

我现在添加了验证以确保传入的字符串是纯数字的(在删除所有逗号之后)

【讨论】:

  • 投反对票原因:动态 sql 是有代价的,无论是在 sql 注入漏洞方面还是在性能方面。我只会将其用作最后的手段。
  • 投反对票?这是一个有效的解决方案,每个答案都有自己的优点和缺点(这个是最短和最容易阅读的)。有一些方法可以防止 sql 注入,性能损失很小,并且只有在重复运行此 SP 时才会明显。
  • 如果我认为动态 sql 是该问题的有效解决方案,我不会拒绝您的回答。此外,虽然可以防止 sql 注入,但您的回答甚至没有提到这个问题,也没有以任何方式保护您建议的解决方案。
  • 按照你这里写的方式,SQL注入是完全易受攻击的,因为你不检查参数。 @ZoharPeled 我同意,DynSQL 可以为 SQL 注入打开。但是,在某些情况下,由于 DynSQL,性能可以大大提高
  • 我现在进行了编辑,以确保传入的字符串不包含逗号和数字以外的任何内容。
猜你喜欢
  • 2015-08-20
  • 2014-08-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-28
  • 2014-09-26
相关资源
最近更新 更多