2017-05-17 4 views
0

すべてのキーワードが複数の列に存在するかどうかを調べるSQLクエリを作成しようとしています。キーワードには、任意の文字列を表す '%'などのワイルドカード文字を含めることができます。すべてのキーワードがSQLの少なくとも1つの列と一致するかどうかを確認してください

など。

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 

私は「ジョー%%コナー%」を検索すると、私は少なくとも1つの列は、「ジョー%」および少なくとも1つの列が「%コナー%」含まれており、私がする必要が含まれているすべての行を検索する必要がありますすべてのキーワードが少なくとも1つの列と一致していることを確認してください。

テーブルでフルテキスト検索を使用することはできません。そして、私はすべての列を連結して、単語の始めがjoで始まらなければならないことを述べている検索語のワイルドカードのために、すべての列が含まれているかどうかを確認することはできません。

テーブルのプロパティなどを変更せずにSQL Server 2012でこの種の検索を行う良い方法はありますか?

+0

first_nameが 'mary'でlast_nameが 'joconnor'の場合はどうなりますか?それは一致ですか? 'jo%'と '%connor%'を検証しますが、それは同じ列です。 – Horaciux

+0

番号。スペースは単語 – Asagohan

+0

を区切りますが、それはワイルドカード検索でなければなりませんか?あなたが有限数の列を持っているならば、ORを行うことができます。 – maSTAShuFu

答えて

1

することで、検索し、詳細にすべてのフィールドを持っていない一つの選択肢であり、それはすべてのヒットレコードのみが返されます、

ここでは、私の解析機能を使用しましたが、インラインクエリに簡単に変換できます。

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 

parse関数興味

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,< & >',',') 
01の場合

EDIT - XMLなしオプション - (XMLよりもはるかにperforant)

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]に戻って区切りを置くが、これは%サラ・コナー」のような検索を妨げます% '。個人的に、私はPIPEのようなトークンを好むが、それは選択肢である。さらに、日付、または/および/または数値を検索することができます。

+0

私はこれが動作すると思うが、それは非常に遅いです。 6500件のレコードが完了するまでに20秒かかります。誰も私により良いクエリを与えることができないならば、私は答えを受け入れるでしょう。 – Asagohan

+0

私は慣れているものよりも複雑なので、何が起こっているのかを正確に理解するために、クエリを実行する必要があります。しかし、XMLに変換する必要はありますか?私はこれが物事を遅くするかもしれないと信じています。 – Asagohan

+0

@Asagohan XML部分は確かに最適化されていません。これは単にデータを動的に解除します(すべてのフィールド名を指定する必要はなく、データ型の告発を避けるだけです)。 UNPIVOTはもっと確かにパフォーマンスが良いと言われていますが、20秒間どこにいるのか分かりません。ちょうど楽しみのために、私は20,000行にデータを展開し、1.8秒で結果を得ました(私のラップトップ上)。 –

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); 
+0

私は "どこ"部分がやっている。それはfirst_nameなどを連結することになっていますか?それはそれがであるという形でSQLサーバーでは動作しません – Asagohan

関連する問題