从SQL Server的Where子句中的多个列进行搜索

时间:2014-06-03 16:09:57

标签: sql sql-server stored-procedures sql-execution-plan

我们有一个存储过程,用于允许用户在包含2000万条记录和40列宽的表中进行搜索。他们可以搜索大约20个不同的列(任意组合),所有这些列都在WHERE子句中。

此外,每个列都会检查Null,并且只需要搜索部分数据。

这是一个例子

(
    @FirstName IS NULL
    OR (RTRIM(UPPER(FirstName)) LIKE RTRIM(UPPER(@FirstName)) + '%')
)
AND (@LastName IS NULL)

重写此存储过程的最佳方法是什么?我应该将此存储过程分解为多个小存储过程吗?如果是这样的话?我需要允许用户搜索

当我查看执行计划时,无论传递什么列,它总是进行索引扫描

3 个答案:

答案 0 :(得分:1)

要使用动态SQL路由,请使用以下内容:

CREATE PROCEDURE dbo.SearchSomeTable 
    @FirstName  VARCHAR(20),
    @LastName   VARCHAR(20),
    @AnotherCol INT
AS
BEGIN

    DECLARE @SQL NVARCHAR(MAX) = N'SELECT SomeColumn FROM SomeTable WHERE 1 = 1',
            @ParamDefinition NVARCHAR(MAX) = N'@FirstName VARCHAR(20),
                                                @LastName VARCHAR(20),
                                                @AnotherCol INT';

    IF @FirstName IS NOT NULL
        @SQL = @SQL + ' AND FirstName = @FirstName';


    IF @LastName IS NOT NULL
        @SQL = @SQL + ' AND LastName = @LastName';

    IF @AnotherCol IS NOT NULL
        @SQL = @SQL + ' AND AnotherCol = @AnotherCol';

    EXECUTE sp_executesql @sql, @ParamDefinition, @FirstName, @LastName, @AnotherCol;

END

否则,您需要使用OPTION (RECOMPILE) query hint强制查询在每次运行时重新编译,以获得您传递的特定参数的最佳计划。

答案 1 :(得分:1)

几年前,我确实有这种情况,数百万行和众多的过滤器参数,最好的方法是使用动态sql。根据具有值的参数构造SQL语句,然后执行SQL语句。 (EXEC sp_executesql @sql)

sql语句的select子句是静态的,但from子句和where子句基于参数。

CREATE PROCEDURE dbo.DynamicSearch
    @FirstName     VARCHAR(20),
    @LastName      VARCHAR(20),
    @CompanyName   VARCHAR(50)

AS
BEGIN
DECLARE @SQL NVARCHAR(MAX) = N''
DECLARE @Select NVARCHAR(MAX) = N'SELECT ColA, ColB, ColC, ColD '
DECLARE @From NVARCHAR(MAX) = N'From Person'
DECLARE @Where NVARCHAR(MAX) = N''

IF @FirstName IS NOT NULL
    Begin
        Set @Where = @Where + 'FirstName = ''' + @FirstName + ''''
    End

IF @LastName IS NOT NULL
    Begin
        if len(@Where) > 0 
            Begin
                Set @Where = @Where + ' AND '       
            End

        Set @Where = @Where + 'LastName = ''' + @LastName + ''''
    End


IF @CompanyName IS NOT NULL
    Begin
        if len(@Where) > 0 
            Begin
                Set @Where = @Where + ' AND '       
            End

        Set @From = @From + ' inner join Company on person.companyid = company.companyid '
        Set @Where = @Where + 'company.CompanyName = ''' + @CompanyName + ''''
    End

Set @SQL = @Select + @From + @Where

EXECUTE sp_executesql @sql

END

答案 2 :(得分:0)

另一种可能性,尽管以上可能是最合乎逻辑的,是创建一个或多个“搜索”条件表,您在其中插入用户选择,然后对搜索条件执行LEFT JOINS。对于存在等效性且最好是小数据类型(如int)的情况,您只能这样做。对于字符串比较,就性能而言,这种类型的连接可能是可怕的。上述建议(dynamic-sql)的唯一问题是计划缓存膨胀的可能性,因为每次执行只与现有计划存在一个差异,将导致生成新计划。