接受多个 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 存储过程的主要内容,如果未能解决你的问题,请参考以下文章

如何从 T-SQL 存储过程返回表

SQL Server存储过程简介

执行带返回值的存储过程

将 Dictionary<string,int> 传递给存储过程 T-SQL

有无参数之存储过程

将表中的列值作为参数传递给存储过程 sql