【问题标题】:T-SQL stored procedure that accepts multiple Id values接受多个 Id 值的 T-SQL 存储过程
【发布时间】:2010-09-07 18:42:58
【问题描述】:

是否有一种优雅的方式来处理将 id 列表作为参数传递给存储过程?

例如,我希望我的存储过程返回部门 1、2、5、7、20。过去,我传入了一个以逗号分隔的 id 列表,就像下面的代码一样,但这样做真的很脏。

我认为 SQL Server 2005 是我唯一适用的限制。

create procedure getDepartments
  @DepartmentIds varchar(max)
as
  declare @Sql varchar(max)     
  select @Sql = 'select [Name] from Department where DepartmentId in (' + @DepartmentIds + ')'
  exec(@Sql)

【问题讨论】:

标签: sql-server tsql stored-procedures


【解决方案1】:

是的,您当前的解决方案容易受到 SQL 注入攻击。

我发现的最佳解决方案是使用将文本拆分为单词的功能(此处发布了一些,或者您可以使用this one from my blog),然后将其加入您的表格。比如:

SELECT d.[Name]
FROM Department d
    JOIN dbo.SplitWords(@DepartmentIds) w ON w.Value = d.DepartmentId

【讨论】:

  • 我不确定它是否“容易受到 SQL 注入攻击”,除非存储的过程可以直接从不受信任的客户端调用,在这种情况下你会遇到更大的问题。服务层代码应该从强类型数据(例如 int[] departmentIds)生成@DepartmentIds 字符串,在这种情况下你会没事的。
  • 很棒的解决方案,@Matt Hamilton。不知道这是否对任何人都有帮助,但是当我使用“join dbo.SplitWords(@MyParameterArray) p ON CHARINDEX(p.value, d.MyFieldToSearch) > 0”搜索文本字段时,我在 SQL Server 2008r 上得到了更准确的结果
【解决方案2】:

您可以使用 XML。

例如

declare @xmlstring as  varchar(100) 
set @xmlstring = '<args><arg value="42" /><arg2>-1</arg2></args>' 

declare @docid int 

exec sp_xml_preparedocument @docid output, @xmlstring

select  [id],parentid,nodetype,localname,[text]
from    openxml(@docid, '/args', 1) 

命令sp_xml_preparedocument是内置的。

这将产生输出:

id  parentid    nodetype    localname   text
0   NULL        1           args        NULL
2   0           1           arg         NULL
3   2           2           value       NULL
5   3           3           #text       42
4   0           1           arg2        NULL
6   4           3           #text       -1

它拥有您所需要的全部(更多?)。

【讨论】:

    【解决方案3】:

    在过去的 16 年里,Erland Sommarskog 一直为这个问题提供权威答案:Arrays and Lists in SQL Server

    至少有十几种方法可以将数组或列表传递给查询;每个都有自己独特的优点和缺点。

    我真的无法向read the article 推荐足够多的东西来了解所有这些选项之间的权衡。

    【讨论】:

      【解决方案4】:

      如果您要大量使用这些值,您可能需要考虑的一种方法是先将它们写入临时表。然后你就像平常一样加入它。

      这样,你只解析一次。

      使用“拆分”UDF 之一是最简单的,但是很多人都发布了这些示例,我想我会走一条不同的路线;)

      此示例将创建一个临时表供您加入 (#tmpDept),并用您传入的部门 ID 填充它。我假设您用逗号分隔它们,但您可以 -- 当然可以-- 随意更改。

      IF OBJECT_ID('tempdb..#tmpDept', 'U') IS NOT NULL
      BEGIN
          DROP TABLE #tmpDept
      END
      
      SET @DepartmentIDs=REPLACE(@DepartmentIDs,' ','')
      
      CREATE TABLE #tmpDept (DeptID INT)
      DECLARE @DeptID INT
      IF IsNumeric(@DepartmentIDs)=1
      BEGIN
          SET @DeptID=@DepartmentIDs
          INSERT INTO #tmpDept (DeptID) SELECT @DeptID
      END
      ELSE
      BEGIN
              WHILE CHARINDEX(',',@DepartmentIDs)>0
              BEGIN
                  SET @DeptID=LEFT(@DepartmentIDs,CHARINDEX(',',@DepartmentIDs)-1)
                  SET @DepartmentIDs=RIGHT(@DepartmentIDs,LEN(@DepartmentIDs)-CHARINDEX(',',@DepartmentIDs))
                  INSERT INTO #tmpDept (DeptID) SELECT @DeptID
              END
      END
      

      这将允许您传入一个部门 id,多个 id 之间带有逗号,甚至多个 id 之间带有逗号和空格。

      所以如果你做了类似的事情:

      SELECT Dept.Name 
      FROM Departments 
      JOIN #tmpDept ON Departments.DepartmentID=#tmpDept.DeptID
      ORDER BY Dept.Name
      

      您会看到您传入的所有部门 ID 的名称...

      再次,这可以通过使用一个函数来填充临时表来简化......我主要是没有一个只是为了消除一些无聊:-P

      -- 凯文·费尔柴尔德

      【讨论】:

        【解决方案5】:

        一个超快的 XML 方法,如果你想使用存储过程并传递逗号分隔的部门 ID 列表:

        Declare @XMLList xml
        SET @XMLList=cast('<i>'+replace(@DepartmentIDs,',','</i><i>')+'</i>' as xml)
        SELECT x.i.value('.','varchar(5)') from @XMLList.nodes('i') x(i))
        

        所有功劳归 Guru Brad Schulz's Blog

        【讨论】:

          猜你喜欢
          • 2018-10-03
          • 2013-05-28
          • 2011-02-22
          • 2017-08-18
          • 1970-01-01
          • 2011-08-03
          • 1970-01-01
          • 2021-12-26
          • 2014-06-07
          相关资源
          最近更新 更多