在T-SQL中使用任意数量的参数

时间:2008-11-18 22:44:36

标签: sql sql-server tsql

是否可以创建一个参数化的SQL语句,该语句将采用任意数量的参数?我试图允许用户根据多个关键字过滤列表,每个关键字用分号分隔。因此,输入将类似于“奥克兰;城市;规划”,WHERE子句将出现与下面相同的内容:

WHERE ProjectName LIKE '%Oakland%' AND ProjectName Like '%City%' AND ProjectName Like '%Planning%'

使用连接创建这样的列表真的很容易,但由于SQL注入漏洞,我不想采用这种方法。我有什么选择?我是否创建了一堆参数,希望用户不要尝试使用我定义的更多参数?或者有没有办法安全地创建参数化SQL?

性能不是问题,因为该表现在只有大约900行,并且不会很快增长,可能每年50到100行。

9 个答案:

答案 0 :(得分:6)

一个基本的概念验证...实际代码会少一些,但由于我不知道你的表/字段名称,这是完整的代码,所以任何人都可以验证它的工作原理,调整它等等。

--Search Parameters

DECLARE @SearchString VARCHAR(MAX)
SET @SearchString='Oakland;City;Planning' --Using your example search
DECLARE @Delim CHAR(1)
SET @Delim=';' --Using your deliminator from the example

--I didn't know your table name, so I'm making it... along with a few extra rows...

DECLARE @Projects TABLE (ProjectID INT, ProjectName VARCHAR(200))
INSERT INTO @Projects (ProjectID, ProjectName) SELECT 1, 'Oakland City Planning'
INSERT INTO @Projects (ProjectID, ProjectName) SELECT 2, 'Oakland City Construction'
INSERT INTO @Projects (ProjectID, ProjectName) SELECT 3, 'Skunk Works'
INSERT INTO @Projects (ProjectID, ProjectName) SELECT 4, 'Oakland Town Hall'
INSERT INTO @Projects (ProjectID, ProjectName) SELECT 5, 'Oakland Mall'
INSERT INTO @Projects (ProjectID, ProjectName) SELECT 6, 'StackOverflow Answer Planning'

--*** MAIN PROGRAM CODE STARTS HERE ***

DECLARE @Keywords TABLE (Keyword VARCHAR(MAX))

DECLARE @index int 
SET @index = -1 

--Each keyword gets inserted into the table
--Single keywords are handled, but I did not add code to remove duplicates
--since that affects performance only, not the result.

WHILE (LEN(@SearchString) > 0) 
  BEGIN  
    SET @index = CHARINDEX(@Delim , @SearchString)  
    IF (@index = 0) AND (LEN(@SearchString) > 0)  
      BEGIN   
        INSERT INTO @Keywords VALUES (@SearchString)
          BREAK  
      END  
    IF (@index > 1)  
      BEGIN   
        INSERT INTO @Keywords VALUES (LEFT(@SearchString, @index - 1))   
        SET @SearchString = RIGHT(@SearchString, (LEN(@SearchString) - @index))  
      END  
    ELSE 
      SET @SearchString = RIGHT(@SearchString, (LEN(@SearchString) - @index)) 
END


--This way, only a project with all of our keywords will be shown...

SELECT * 
FROM @Projects
WHERE ProjectID NOT IN (SELECT ProjectID FROM @Projects Projects INNER JOIN @Keywords Keywords ON CHARINDEX(Keywords.Keyword,Projects.ProjectName)=0)

我决定将几个不同的答案混合成一个:-P

这假设您将传递一个分隔的搜索关键字字符串列表(通过@SearchString传入)作为 VARCHAR(MAX),实际上 - 您赢了“对关键字搜索设置限制

每个关键字都会从列表中细分,会添加到关键字表格中。您可能希望添加代码以删除重复的关键字,但在我的示例中不会受到影响。效果稍差,因为我们只需要为每个关键字评估一次,理想情况下。

从那里,任何不属于项目名称的关键字都会从列表中删除该项目 ...

所以搜索“奥克兰”给出了4个结果,但“奥克兰;城市;规划”只给出了1个结果。

您也可以更改分隔符,因此它可以使用空格而不是分号。或者不管你的船漂浮......

此外,由于连接而不是动态SQL,它不像您担心的那样冒着SQL注入的风险。

答案 1 :(得分:2)

您可能还需要考虑全文搜索,并使用CONTAINSCONTAINSTABLE获得更“自然”的搜索功能。

对于1K行可能有点过分,但是写入并且不容易被注入破坏。

答案 2 :(得分:1)

通常只需将列表作为由逗号(csv样式)分隔的字符串传递,在循环中解析该字符串并动态构建查询。

上面的帖子也是正确的,也许最好的方法不是T-SQL,而是业务/应用层。

答案 3 :(得分:1)

使用XML数据类型来包含参数怎么样? 它可以在运行时无限制地组装......

我传入一个未知数量的PK进行表更新,然后将它们泵入临时表。 然后很容易更新PKTempTable中PK的位置。

以下是解析XML数据类型的代码......

    INSERT INTO #ERXMLRead (ExpenseReportID)
    SELECT ParamValues.ID.value('.','VARCHAR(20)')
    FROM @ExpenseReportIDs.nodes('/Root/ExpenseReportID') as ParamValues(ID)

答案 4 :(得分:0)

使用像NHibernate这样的工具,您可以安全地动态构建查询,而无需存储过程。

Frans Bouma有一个excellent article关于存储过程与动态sql的关系,使用SQL生成器的一些好处是使用手工生成的语句

答案 5 :(得分:0)

如果您使用存储过程可以包含参数的默认值,那么您可以选择传递它们或不在客户端代码中传递它们,但您仍然必须在存储过程中单独声明它们...只有当你使用存储过程时,你可以将一个参数作为分隔的值字符串传递,并解析出sproc中的各个值(有一些“标准”T-SQL函数可用于拆分记录到你的动态表变量)

答案 6 :(得分:0)

如果您使用SQL Server 2008,请查看此文章passing a table valued parameter

答案 7 :(得分:0)

无论你走哪条路,都要注意SQL Server参数限制:~2000参数。

答案 8 :(得分:0)

与其他一些答案类似,您可以解析分隔的字符串或XML文档。请参阅this excellent link,它使用SQL Server演示这两种方法。