好吧,让我重申我对你的问题的理解:你想要一个存储过程,可以获取可变数量的参数并传回匹配的顶行偏好的加权顺序参数在SQL Server 2005上传递。
理想情况下,它将使用WHERE子句来防止全表扫描以及利用索引,并将“短路”搜索 - 您不想搜索所有可能的组合,如果可以早找到的话。也许我们也可以允许其他比较器比如=,例如> =用于日期,LIKE用于字符串等。
一种可能的方式是像在this article中那样传递XML参数并使用.Net存储过程,但让我们保持原样现在是香草T-SQL。
这看起来对我来说,在参数二进制搜索:搜索所有参数,然后删除最后一个,然后挂断第二个最后一个,但包括最后一个,等
让我们传递的参数作为由于存储过程不允许将数组作为参数传递,因此使用分隔字符串。这将允许我们在我们的存储过程中获取可变数量的参数,而不需要为每个参数变化存储过程。
为了让任何形式的比较,我们会通过整个WHERE子句列表,就像这样:标题LIKE“%%的东西”
传递多个参数指在一个字符串界定他们。我们将使用代字符〜字符来分隔参数,如下所示:author ='Chris Latta'〜title like'%something%'〜pages> = 100
然后,这只是一个做二进制加权的问题搜索满足我们的有序参数列表的第一行(希望存储过程带有注释是不言自明的,但如果没有,请告诉我)。请注意,总是保证结果(假定您的表至少有一行),因为最后的搜索是无参数的。
这里是存储过程的代码:
CREATE PROCEDURE FirstMatch
@SearchParams VARCHAR(2000)
AS
BEGIN
DECLARE @SQLstmt NVARCHAR(2000)
DECLARE @WhereClause NVARCHAR(2000)
DECLARE @OrderByClause NVARCHAR(500)
DECLARE @NumParams INT
DECLARE @Pos INT
DECLARE @BinarySearch INT
DECLARE @Rows INT
-- Create a temporary table to store our parameters
CREATE TABLE #params
(
BitMask int, -- Uniquely identifying bit mask
FieldName VARCHAR(100), -- The field name for use in the ORDER BY clause
WhereClause VARCHAR(100) -- The bit to use in the WHERE clause
)
-- Temporary table identical to our result set (the books table) so intermediate results arent output
CREATE TABLE #junk
(
id INT,
author VARCHAR(50),
title VARCHAR(50),
printed DATETIME,
pages INT
)
-- Ill use tilde ~ as the delimiter that separates parameters
SET @SearchParams = LTRIM(RTRIM(@SearchParams))+ '~'
SET @Pos = CHARINDEX('~', @SearchParams, 1)
SET @NumParams = 0
-- Populate the #params table with the delimited parameters passed
IF REPLACE(@SearchParams, '~', '') <> ''
BEGIN
WHILE @Pos > 0
BEGIN
SET @NumParams = @NumParams + 1
SET @WhereClause = LTRIM(RTRIM(LEFT(@SearchParams, @Pos - 1)))
IF @WhereClause <> ''
BEGIN
-- This assumes your field names dont have spaces and that you leave a space between the field name and the comparator
INSERT INTO #params (BitMask, FieldName, WhereClause) VALUES (POWER(2, @NumParams - 1), LTRIM(RTRIM(LEFT(@WhereClause, CHARINDEX(' ', @WhereClause, 1) - 1))), @WhereClause)
END
SET @SearchParams = RIGHT(@SearchParams, LEN(@SearchParams) - @Pos)
SET @Pos = CHARINDEX('~', @SearchParams, 1)
END
END
-- Set the binary search to search from all parameters down to one in order of preference
SET @BinarySearch = POWER(2, @NumParams)
SET @Rows = 0
WHILE (@BinarySearch > 0) AND (@Rows = 0)
BEGIN
SET @BinarySearch = @BinarySearch - 1
SET @WhereClause = ' WHERE '
SET @OrderByClause = ' ORDER BY '
SELECT @OrderByClause = @OrderByClause + FieldName + ', ' FROM #params WHERE (@BinarySearch & BitMask) = BitMask ORDER BY BitMask
SET @OrderByClause = LEFT(@OrderByClause, LEN(@OrderByClause) - 1) -- Remove the trailing comma
SELECT @WhereClause = @WhereClause + WhereClause + ' AND ' FROM #params WHERE (@BinarySearch & BitMask) = BitMask ORDER BY BitMask
SET @WhereClause = LEFT(@WhereClause, LEN(@WhereClause) - 4) -- Remove the trailing AND
IF @BinarySearch = 0
BEGIN
-- If nothing found so far, return the top row in the order of the parameters fields
SET @WhereClause = ''
-- Use the full order sequence of fields to return the results
SET @OrderByClause = ' ORDER BY '
SELECT @OrderByClause = @OrderByClause + FieldName + ', ' FROM #params ORDER BY BitMask
SET @OrderByClause = LEFT(@OrderByClause, LEN(@OrderByClause) - 1) -- Remove the trailing comma
END
-- Find out if there are any results for this search
SET @SQLstmt = 'SELECT TOP 1 id, author, title, printed, pages INTO #junk FROM books' + @WhereClause + @OrderByClause
Exec (@SQLstmt)
SET @Rows = @@RowCount
END
-- Stop the result set being eaten by the junk table
SET @SQLstmt = REPLACE(@SQLstmt, 'INTO #junk ', '')
-- Uncomment the next line to see the SQL you are producing
--PRINT @SQLstmt
-- This gives the result set
Exec (@SQLstmt)
END
此存储过程称为像这样:
FirstMatch 'author = ''Chris Latta''~pages > 100~title like ''%something%'''
有你有它 - 对顶级结果加权一个完全可扩展,优化搜索优先顺序。这是一个有趣的问题,并展示了您可以使用本机T-SQL实现的功能。
跟这个有几个小问题:
- 它依赖于调用者知道,他们必须在字段名称后留出空间为参数正常工作
- 你不能有场用空格名字在其中 - 可以解决的一些努力
- 它假定相关的排序顺序总是上升
- 下一个程序员有来看待这个过程会觉得你疯了:)
使用AND连接WHERE子句中的条件时发生了什么? – 2008-12-22 23:03:25
@JL这是否有趣? – 2008-12-23 19:11:48