我正在尝试编写一个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中进行这种搜索而不改变表属性等?
答案 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);