接受多个Id值的T-SQL存储过程

有没有一个优雅的方式来处理传递一个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) 

Erland Sommarskog在过去的16年里一直保持对这个问题的权威答案: SQL Server中的数组和列表

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

  • 表值参数 。 SQL Server 2008及更高版本,可能最接近通用“最佳”方法。
  • 迭代法 。 传递一个分隔的string并循环。
  • 使用CLR 。 SQL Server 2005和更高版本从.NET语言只。
  • XML 。 非常适合插入多行; 可能是SELECTs矫枉过正。
  • 数字表 。 比简单的迭代方法更高的性能/复杂度。
  • 固定长度的元素 。 固定长度提高了分隔string的速度
  • 数字的function 。 数字表的变化和固定长度,其中数字是在函数中生成的,而不是从表格中获取的。
  • recursion公用表expression式 (CTE)。 SQL Server 2005及更高版本,仍然不是太复杂,性能也比迭代方法更高。
  • dynamicSQL 。 可能会很慢,并有安全隐患。
  • 通过列表作为许多参数 。 单调乏味,容易出错,但很简单。
  • 真的很慢的方法 。 使用charindex,patindex或LIKE的方法。

我真的不能推荐阅读这篇文章来学习所有这些select之间的权衡。

是的,你目前的解决scheme很容易发生SQL注入攻击。

我find的最好的解决scheme是使用一个function,将文本拆分成单词(这里有一些发布,或者可以使用我的博客中的这个 ),然后将它们join到表格中。 就像是:

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

你可以使用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值为NULL
 5 3 3 #text 42
 4 0 1 arg2 NULL
 6 4 3 #text -1

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

你可能要考虑的一个方法是,如果你要使用这些值,那么首先将它们写入临时表。 那么你就像正常一样join。

这样,你只parsing一次。

使用其中一个“Split”UDF是最容易的,但是有那么多人张贴了这些例子,我想我会采取不同的路线;)

这个例子将创build一个临时表供您join(#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

– 凯文·仙童

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

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

所有功劳都归功于大师布拉德·舒尔茨的博客

试试这个:

 @list_of_params varchar(20) -- value 1, 2, 5, 7, 20 SELECT d.[Name] FROM Department d where @list_of_params like ('%'+ CONVERT(VARCHAR(10),d.Id) +'%') 

很简单。