是否可以创建一个参数化的SQL语句,该语句将采用任意数量的参数?我试图允许用户根据多个关键字过滤列表,每个关键字用分号分隔。因此,输入将类似于“奥克兰;城市;规划”,WHERE子句将出现与下面相同的内容:
WHERE ProjectName LIKE '%Oakland%' AND ProjectName Like '%City%' AND ProjectName Like '%Planning%'
使用连接创建这样的列表真的很容易,但由于SQL注入漏洞,我不想采用这种方法。我有什么选择?我是否创建了一堆参数,希望用户不要尝试使用我定义的更多参数?或者有没有办法安全地创建参数化SQL?
性能不是问题,因为该表现在只有大约900行,并且不会很快增长,可能每年50到100行。
答案 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)
您可能还需要考虑全文搜索,并使用CONTAINS
或CONTAINSTABLE
获得更“自然”的搜索功能。
对于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演示这两种方法。