检查所有关键字是否与SQL中的至少一列匹配

时间:2017-05-17 00:14:27

标签: sql sql-server sql-server-2012

我正在尝试编写一个SQL查询,它将搜索以检查所有关键字是否存在于多个列中。关键字可能包含通配符,例如'%'来表示任何字符串。

E.g。

first_name  | last_name | age   | height    | mother's name
-------------------------------------------------------
 mary       | jones     | 19    | 170       | sally jane     
 john       | doe       | 43    | 165       | sarah connor
 john       | connor    | 17    | 173       | sarah connor
 joe        | bloe      | 32    | 173       | sarah connor
 john       | connor    | 32    | 165       | sarah connor

如果我搜索'jo %% connor%',我需要查找至少有一列包含'jo%'并且至少有一列包含'%connor%'的所有行,我需要确保所有这些关键字至少匹配了一列。

我不能在桌面上使用全文搜索。而且我认为我不能连接所有列并检查它是否包含所有单词,因为搜索条件中的通配符可能表示单词的开头必须以jo开头。

有没有一种很好的方法在SQL Server 2012中进行这种搜索而不改变表属性等?

2 个答案:

答案 0 :(得分:1)

以下是一个选项,您无需详细说明要搜索的所有字段,并且只返回所有匹配的记录,同时尊重各个搜索模式

现在,我使用了我的Parse函数,但可以很容易地转换为内联查询。

示例

Declare @YourTable Table ([first_name] varchar(50),[last_name] varchar(50),[age] int,[height] int,[mother_name] varchar(50))
Insert Into @YourTable Values
 ('mary','jones',19,170,'sally jane')
,('john','doe',43,165,'sarah connor')
,('john','connor',17,173,'sarah connor')
,('joe','bloe',32,173,'sarah connor')
,('john','connor',32,165,'sarah connor')


Declare @Search varchar(max) = 'jo% %connor%'

;with cte as (
                Select *,MaxHit=max(RetSeq) over () From [dbo].[udf-Str-Parse](@Search,' ')
             )
Select A.* 
 From @YourTable A
 Cross Apply ( Select XMLData=convert(xml,(Select A.* For XML RAW))) B
 Cross Apply (
                Select Hits=count(*)
                  From (
                        Select Value  = attr.value('.','varchar(max)') 
                         From  B.XMLData.nodes('/row') as A(r)
                         Cross Apply A.r.nodes('./@*') AS B(attr)
                        ) C1
                 Join cte C2 on patindex(C2.RetVal,Value)>0
                 Having count(Distinct C2.RetSeq)>=max(C2.MaxHit)
             ) C

<强>返回

first_name  last_name   age height  mother_name
john        doe         43  165     sarah connor
john        connor      17  173     sarah connor
joe         bloe        32  173     sarah connor
john        connor      32  165     sarah connor

感兴趣的解析功能

CREATE FUNCTION [dbo].[udf-Str-Parse] (@String varchar(max),@Delimiter varchar(10))
Returns Table 
As
Return (  
    Select RetSeq = Row_Number() over (Order By (Select null))
          ,RetVal = LTrim(RTrim(B.i.value('(./text())[1]', 'varchar(max)')))
    From  (Select x = Cast('<x>' + replace((Select replace(@String,@Delimiter,'§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml).query('.')) as A 
    Cross Apply x.nodes('x') AS B(i)
);
--Thanks Shnugo for making this XML safe
--Select * from [dbo].[udf-Str-Parse]('Dog,Cat,House,Car',',')
--Select * from [dbo].[udf-Str-Parse]('John Cappelletti was here',' ')
--Select * from [dbo].[udf-Str-Parse]('this,is,<test>,for,< & >',',')
  

编辑 - 没有XML的选项 - (比XML更加健全)

Declare @Search varchar(max) = 'jo% %connor%'

;with cte as (
                Select *,MaxHit=max(RetSeq) over () From [dbo].[udf-Str-Parse](@Search,' ')
             )
Select A.*,C.*
 From #Temp A
 Cross Apply (
                Select Hits=count(Distinct C2.RetSeq)
                  From ( values (A.[first_name])
                               ,(A.[last_name])
                               ,(concat('',A.[age]))
                               ,(concat('',A.[height]))
                               ,(A.[mother_name])
                        ) C1 (Value)
                 Join cte C2 on patindex(C2.RetVal,Value)>0
                 Having count(Distinct C2.RetSeq)>=max(C2.MaxHit)
             ) C

注意:我将分隔符重新设置为[SPACE],但这会阻止搜索类似于&#39;%sarah connor%&#39;。就个人而言,我更喜欢像PIPE这样的令牌,但这是一种选择。您还可以搜索日期或/或数字。

答案 1 :(得分:0)

这不是一件容易的事,但你可以拆分搜索字符串(假设没有模式有空格)。然后,假设每行有一个唯一的id:

with s(pattern) as (
      select *
      from dbo.split(@str, ' ')
     )
select t.*
from t cross apply
     (select count(*) as cnt
      from s
      where (first_name + last_name + cast(age as varchar(255)) + '|' + cast(height as varchar(255)) + '|' + mothername) like concat('%', s.pattern, '%')
     ) s
where s.cnt = (select count(*) from s);