接受多个 Id 值的 T-SQL 存储过程
Posted
技术标签:
【中文标题】接受多个 Id 值的 T-SQL 存储过程【英文标题】:T-SQL stored procedure that accepts multiple Id values 【发布时间】: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)
【问题讨论】:
Here 是我刚刚找到的 XML 方法的变体。 如果您使用的是 SQL Server 2008,则可以使用表值参数。 http://www.sqlteam.com/article/sql-server-2008-table-valued-parameters 这对我很有帮助:sqlmag.com/t-sql/passing-multivalued-variables-stored-procedure 【参考方案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。
至少有十几种方法可以将数组或列表传递给查询;每个都有自己独特的优点和缺点。
Table-Valued Parameters。仅限 SQL Server 2008 及更高版本,可能是最接近通用“最佳”方法的版本。 The Iterative Method。传递一个分隔字符串并循环遍历它。 Using the CLR。仅限 .NET 语言的 SQL Server 2005 及更高版本。 XML。非常适合插入多行;对于 SELECT 来说可能有点过分了。 Table of Numbers。比简单的迭代方法具有更高的性能/复杂性。 Fixed-length Elements。固定长度提高了分隔字符串的速度 Function of Numbers。数字表和固定长度的变体,其中数字是在函数中生成的,而不是从表格中获取的。 Recursive Common Table Expression (CTE)。 SQL Server 2005 及更高版本,与迭代方法相比仍然不太复杂且性能更高。 Dynamic SQL。可能很慢并且有安全隐患。 以Many Parameters 传递列表。繁琐且容易出错,但很简单。 Really Slow Methods。使用 charindex、patindex 或 LIKE 的方法。我真的无法向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
【讨论】:
以上是关于接受多个 Id 值的 T-SQL 存储过程的主要内容,如果未能解决你的问题,请参考以下文章