跨多个列搜索实现

时间:2012-03-26 17:23:02

标签: sql-server-2008

假设我有一张表:

Persons  
  FirstName varchar(50)  
  LastName varchar(50)
  EmailAddress varchar(200)  

现在,假设我有一个谷歌搜索框;换句话说,只有一个带搜索按钮的文本框。

通常我们会这样做:

declare @searchTerm varchar(50)
set @searchTerm = 'tom'

select *
  from Persons
  where (FirstName = @searchTerm) 
     or (LastName = @searchTerm)
     or (EmailAddress = @searchTerm)

我想做的是能够将名字和姓氏(例如)传递给@searchTerm变量,但我的大脑只是不想构建那个查询。 ;)

例如:

declare @searchTerm varchar(50)
set @searchTerm = 'tom smith'

select *
  from Persons
  where ????

这个想法是返回'tom'或'smith'出现在这些字段中的所有记录。

2 个答案:

答案 0 :(得分:5)

这是一个非常复杂的主题,具有许多微妙的性能影响。你真的需要阅读Erland Sommarskog的这些优秀文章:

Dynamic Search Conditions in T-SQL

The Curse and Blessings of Dynamic SQL

由于没有“一刀切”的查询方法,因此在执行此操作时会产生微妙的性能影响。如果您希望不仅仅是让查询返回正确的答案,无论它有多慢,请查看以下文章:Dynamic Search Conditions in T-SQL by Erland Sommarskog。它涵盖了每种方法,并详细介绍了每种方法的PRO和Cons。

如果您可以确定搜索列的最小和最大可能范围,并且搜索列为NOT NULL,那么您可以做得比(@Search IS NULL或Col = @ Search)更好,{{3} }。但是你应该阅读整篇文章,有很多变化取决于你的情况,你真的需要学习多种方法以及何时使用它们。

如果要在单个字符串参数中搜索多个术语,则需要拆分该字符串。

您需要创建拆分功能。这就是如何使用拆分功能:

SELECT
    *
    FROM YourTable                               y
    INNER JOIN dbo.yourSplitFunction(@Parameter) s ON y.ID=s.Value

see this area of the above linked article但是有很多方法可以在SQL Server中拆分字符串,请参阅上一个链接,该链接解释了每个链接的PRO和CON。

要使Numbers Table方法起作用,您需要进行一次性表设置,这将创建一个包含1到10,000行的表Numbers

SELECT TOP 10000 IDENTITY(int,1,1) AS Number
    INTO Numbers
    FROM sys.objects s1
    CROSS JOIN sys.objects s2
ALTER TABLE Numbers ADD CONSTRAINT PK_Numbers PRIMARY KEY CLUSTERED (Number)

设置Numbers表后,创建此拆分功能:

CREATE FUNCTION [dbo].[FN_ListToTable]
(
     @SplitOn  char(1)      --REQUIRED, the character to split the @List string on
    ,@List     varchar(8000)--REQUIRED, the list to split apart
)
RETURNS TABLE
AS
RETURN 
(

    ----------------
    --SINGLE QUERY-- --this will not return empty rows
    ----------------
    SELECT
        ListValue
        FROM (SELECT
                  LTRIM(RTRIM(SUBSTRING(List2, number+1, CHARINDEX(@SplitOn, List2, number+1)-number - 1))) AS ListValue
                  FROM (
                           SELECT @SplitOn + @List + @SplitOn AS List2
                       ) AS dt
                      INNER JOIN Numbers n ON n.Number < LEN(dt.List2)
                  WHERE SUBSTRING(List2, number, 1) = @SplitOn
             ) dt2
        WHERE ListValue IS NOT NULL AND ListValue!=''

);
GO 

您现在可以轻松地将CSV字符串拆分为表格并加入其中:

select * from dbo.FN_ListToTable(',','1,2,3,,,4,5,6777,,,')

输出:

ListValue
-----------------------
1
2
3
4
5
6777

(6 row(s) affected)

您可以使用多个搜索条件字符串,如下所示:

DECLARE @Persons table (FirstName varchar(50)  ,  LastName varchar(50),  EmailAddress varchar(200) )
INSERT INTO @Persons VALUES ('aaa','bbb','ccc@ddd.eee')
INSERT INTO @Persons VALUES ('xxx','yyy','zzz@abbba.zzz')
INSERT INTO @Persons VALUES ('aaa','yyy','zzz@zzz.zzz')
INSERT INTO @Persons VALUES ('111','222','333@444.555')

declare @searchTerm varchar(50)
set @searchTerm = 'aaa bbb'

--this should use an index on FirstName and LastName if they exist, no index usage on EmailAddress
select
    p.*  --<<"*" isn't good, only list the columns you need
    FROM @Persons                                      p
        INNER JOIN dbo.FN_ListToTable(' ',@searchTerm) b on p.FirstName=b.Listvalue
UNION
select
    p.*  --<<"*" isn't good, only list the columns you need
    FROM @Persons                                      p
        INNER JOIN dbo.FN_ListToTable(' ',@searchTerm) b on p.LastName=b.Listvalue
UNION
select
    p.*  --<<"*" isn't good, only list the columns you need
    FROM @Persons                                      p
        INNER JOIN dbo.FN_ListToTable(' ',@searchTerm) b on p.EmailAddress like '%'+b.Listvalue+'%'

输出:

FirstName  LastName   EmailAddress
---------- ---------- -------------------------
aaa        bbb        ccc@ddd.eee
aaa        yyy        zzz@zzz.zzz
xxx        yyy        zzz@abbba.zzz

(3 row(s) affected)

此方法适用于任意数量的参数:

aaa
 aaa
aaa bbb
aaa  bbb
aaa bbb eee
aaa bbb eee ddd
aaa   bbb   eee

答案 1 :(得分:0)

您可以尝试这样的事情:

DECLARE
    @searchTerm VARCHAR(100),
    @FirstName VARCHAR(100),
    @LastName VARCHAR(100)
SET
    @searchTerm = 'tom smith'

SELECT
    @FirstName = '%' + SUBSTRING(@searchTerm, 1, CHARINDEX(' ', @searchTerm) - 1) + '%',
    @LastName = '%' + SUBSTRING(@searchTerm, CHARINDEX(' ', @searchTerm) + 1, LEN(@searchTerm)) + '%'

SELECT *
FROM
    Persons
WHERE
    FirstName LIKE @FirstName
OR  LastName LIKE @FirstName
OR  EmailAddress LIKE @FirstName
OR  FirstName LIKE @LastName
OR  LastName LIKE @LastName
OR  EmailAddress LIKE @LastName