好的,让我重申一下我对您的问题的理解:您需要一个存储过程,它可以采用可变数量的参数,并将与 SQL Server 2005 上传递的加权优先顺序中的参数匹配的第一行传回。
理想情况下,它将使用 WHERE 子句来防止全表扫描,并利用索引并将“短路”搜索 - 如果可以及早找到一个,您不希望搜索所有可能的组合。也许我们还可以允许除 = 之外的其他比较器,例如 >= 用于日期,LIKE 用于字符串等。
一种可能的方法是像本文中那样将参数作为 XML 传递并使用 .Net 存储过程,但现在让我们保持普通的普通 T-SQL。
在我看来,这就像对参数的二进制搜索:搜索所有参数,然后删除最后一个,然后删除倒数第二个但包括最后一个,等等。
让我们将参数作为分隔字符串传递,因为存储过程不允许将数组作为参数传递。这将允许我们在存储过程中获取可变数量的参数,而无需为每个参数变化创建一个存储过程。
为了允许进行任何类型的比较,我们将传递整个 WHERE 子句列表,如下所示: title like '%something%'
传递多个参数意味着将它们分隔在一个字符串中。我们将使用波浪号 ~ 字符来分隔参数,如下所示: 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 可以实现的目标。
这有几个小问题:
- 它依赖于调用者知道他们必须在字段名称后留一个空格才能使参数正常工作
- 你不能有带有空格的字段名称 - 可以通过一些努力来修复
- 它假设相关的排序顺序总是升序的
- 下一个必须查看此过程的程序员会认为您疯了 :)