接受多个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) +'%')
很简单。