1

会社のテーブルとカテゴリのテーブルがあります。SQL Server フリー テキスト検索を使用していますが、会社の検索 (名前と説明による) は問題なく機能します。しかし、ここでは、カテゴリ テーブルも含めたいと考えています。

次のようなものを検索したい: ABC 24 Supermarket.

ここで、テーブル内の列とABC 24一致する必要があり、この会社が接続されている名前です。NamecompanySupermarketcategory

今、私はこのようなものを持っています:

DECLARE @SearchString VARCHAR(100) = '"ABC 24 Supermarket"'
SELECT * FROM Company CO
INNER JOIN Category CA
ON CA.CategoryId = CO.CategoryId
WHERE CONTAINS((CO.[Description], CO.[Name]), @SearchString)
AND CONTAINS(CA.[Description], @SearchString)

しかしもちろん、検索文字列が会社テーブルにもカテゴリ テーブルにも見つからないため、これでは何も得られません。自分の会社とカテゴリ テーブルを組み合わせて検索する方法を知っている人はいますか?

以下の Lobo の回答で示唆されているように、文字列を分割するという考えは、実際にはオプションではありません。どの部分がカテゴリに一致するか、どの部分を使用して会社名/説明を一致させる必要があるかがわからないためです。ユーザーは、「スーパーマーケット ABC 24」と入力することもできます。

4

9 に答える 9

4

これを行う正しい方法は、メインテーブルの主キー(例では「会社」)を含むインデックス付きビューと、実際に検索したいすべてのものを含む2番目の列を作成することです。

create View View_FreeTextHelper with schemabinding as
select CO.PrimaryKey,                            -- or whatever your PK is named
       CO.description +' '+CA.description +' '+CO.whatever as Searchtext
  from dbo.company CO join 
       dbo.category CA on CA.CategoryId = CO.CategoryId

テーブルが 2 部構成になっていることに注意してください。これにより、いくつかの制限が生じます。たとえば、関連するすべてのテーブルは同じテーブル スペースにある必要があり、私が覚えている限り、この種の連結では TEXT カラムは許可されません (ただし、キャストすることはできます)。

PrimaryKey列に一意のインデックスを作成します

create unique clustered index [View_Index] 
    on View_FreeTextHelper (PrimaryKey ASC)

最後に、インデックスを作成する唯一の列として「Searchtext」列を使用して、ビューにフルテキスト インデックスを作成します。もちろん、会社名や場所、マネージャーの名前を検索する際に区別したい場合などは、さらに列を追加することもできます (これらを 2 番目の列に連結するだけです)。

データの取得が簡単になりました:

select tbl.RANK,
       co.* 
  from freetextTable(View_FreeTextHelper,Search,'Your searchtext here') tbl
  join company co on tbl.key=co.PrimaryKey
 order by tbl.RANK desc

select top 50また、freetexttable句が最終的に非常に多くの近い結果とそうでない結果を返すため、出力を制限することもできます。

最後に、「off the shelf inc.」のようなものが見つからなくても、混乱しないでください。停止リストに注意してください。これらは非常に一般的な単語のリストであり、意味的な用途 ( などthe) がないため、索引付けされるテキストから削除されます。それらを含めるには、ストップリスト機能を切り替える必要があります。

最後のヒント: 全文は非常に強力ですが、多くの機能、トリック、注意事項があります。テクニックを完全に理解し、望む最高の結果を得るにはかなりの時間がかかります。

楽しむ。

于 2013-09-18T07:02:14.690 に答える
1

列の名前が行ごとに一意であると仮定すると、以下のクエリを使用できます。次の例では、各列に "ABC"、"24"、または "Supermarket" のいずれかの語句を含むすべての行が返されます。

DECLARE @SearchString nvarchar(100) = N'ABC 24 Supermarket'
SET @SearchString = REPLACE(LTRIM(RTRIM(@SearchString)), ' ', '|')
SELECT *
FROM Company CO JOIN Category CA ON CA.CategoryId = CO.CategoryId
WHERE CONTAINS(CO.[Name], @SearchString)
 AND CONTAINS(CO.[Description], @SearchString)
 AND CONTAINS(CA.[Description], @SearchString)

まず、WHERE 句で使用される CONTAINS 述語の検索値を準備する必要があります。この場合、「|」の単語間のスペースを置き換えました。論理演算子 (OR キーワードの代わりに棒記号 (|) を使用して、OR 演算子を表すことができます。)

于 2013-09-19T13:51:58.743 に答える
0

論理を逆にしてみませんか?あなたの例は、フィールド値内で検索文字列を見つけようとしていましたが、本当にやりたいことは、検索文字列内でフィールド値を見つけることですよね?

    DECLARE @SearchString VARCHAR(100) = '"ABC 24 Supermarket"'
SELECT * FROM Company CO
INNER JOIN Category CA
ON CA.CategoryId = CO.CategoryId
WHERE (CONTAINS(@SearchString, CO.[Description]) OR CONTAINS(@SearchString, CO.[Name]))
AND CONTAINS(@SearchString, CA.[Description])
于 2013-09-19T19:18:26.773 に答える
0

これが素晴らしい答えになるかどうかはわかりませんが(私はそれを疑う傾向があります)、取り組むべき問題が欲しかったので、たまたまあなたのものを選んだので、ここに私の解決策があります:

DECLARE @SearchString VARCHAR(100) = '"ABC 24 Supermarket"'

DECLARE @AllItems table (
  SearchItem varchar(100)
  ),
        @x int;

-- break up @SearchString into component search items:
select @x = charindex(' ', @SearchString);
while @x > 0 begin
  insert @AllItems (SearchItem) values (substring(@SearchString, 1, @x - 1));
  select @SearchString = substring(@searchstring, @x + 1);
  select @x = charindex(' ', @Searchstring);
end;
-- add the last item
insert @AllItems (SearchItem) values (@SearchString)


DECLARE @this varchar(100),  -- = current search item
        @found table (       -- table to contain rows matching the current search item
          ID int
          ),
        @usable table (      -- table to contain rows matching all search items
          ID int             --   already tested
          );

--now process search items one-at-a-time
while (select count(*) from @AllItems) > 0 begin
  select @this = min(SearchItem) from @AllItems;
  delete @AllItems where SearchItem = @this;

  if (select count(*) from @usable) = 0 begin  --first search item
    --for the first item, just find the companies matching this item, in either the 
    --company name or description or category description columns:
    insert @found (ID)
    select CO.CompanyID
    from Company CO inner join Category CA on CO.CategoryID = CA.CategoryID
    where contains ((CO.[Description], [CO.[Name]) @this)
      or contains (CA.[Description], @this)
  end 
  else begin                                   --other search items
    -- for subsequent items, its got to match with the company name or description
    -- or category description as above - BUT it's also got to be a company we 
    -- already identified when processing the previous term
    insert @found (ID)
    select CO.CompanyID
    from Company CO inner join Category CA on CO.CategoryID = CA.CategoryID inner join @usable U on CO.CompanyID = U.ID
    where contains ((CO.[Description], [CO.[Name]) @this)
      or contains (CA.[Description], @this)
  end 

  --now clear out and re-populate the usable companies table ready for processing the
  --next search item
  delete @usable;
  insert @usable (ID)
  select ID
  from @found;

  --and clear out the current matches table, ready for the next search item
  delete @found;
end;


--whatever is in @usable now, is a match with all the component search items, so:
select CO.*
from Company CO inner join Category CA on CO.CategoryId = CA.CategoryId inner join @usable U on CO.CompanyID = U.ID;
于 2013-09-13T05:23:04.403 に答える
0

私が以前に書いた答えはうまく機能し、合理的に効率的であるはずですが、一度に1つずつ検索項目を処理し、既存の検索結果内で最初の項目以降の項目のみを検索します。動的SQLを使用して、すべてを一度に実行します。

したがって、ここにあなたの問題に対する別の潜在的な解決策があります。これは、私がすでに投稿した解決策とは無関係であるため、別の回答として入力します。

DECLARE @SearchString VARCHAR(100) = '"ABC 24 Supermarket"'

DECLARE @AllItems table (
  SearchItem varchar(100)
  ),
        @x    int,
        @cmd  varchar(1000),
        @wc   varchar(8000),
        @this varchar(100);

-- break up @SearchString into component search items:
select @x = charindex(' ', @SearchString);
while @x > 0 begin
  insert @AllItems (SearchItem) values (substring(@SearchString, 1, @x - 1));
  select @SearchString = substring(@searchstring, @x + 1);
  select @x = charindex(' ', @Searchstring);
end;
-- add the last item
insert @AllItems (SearchItem) values (@SearchString)

select @cmd = 'select CO.* from Company CO inner join Category CA on CO.CategoryId = CA.CategoryId WHERE';


--now process search items one-at-a-time building up a where clause to plug into @cmd:
while (select count(*) from @AllItems) > 0 begin
  select @this = min(SearchItem) from @AllItems;
  delete @AllItems where SearchItem = @this;

  select @wc = @wc +
    'AND (contains ((CO.[Description], [CO.[Name]) ''' + @this + ''') or contains (CA.[Description], ''' + @this + ''') '
end;

--ready to go:
exec (@cmd + substring(@wc, 4));  --substring removes first AND
于 2013-09-13T06:06:08.480 に答える
0

ここでの考え方は、文字列をさまざまな変数に解析してから、他の変数で一致するものを検索するというものです。

DECLARE @SearchString VARCHAR(100) = '"ABC 24 Supermarket"', @A varchar(100), @B varchar(100), @C varchar(100),@index int

set @A = Substring(@searchString, 1, PATINDEX('% %', @searchString) -1)
set @index = PATINDEX('% %', @searchString) + 1
set @B = Substring(@searchString, @index, PATINDEX('% %', @substring(@searchstring, @index, 100)) -1)
Set @index = PATINDEX('% %', @substring(@searchstring, @index, 100)) + 1
set @C = Substring(@searchString, @index, PATINDEX('% %', @substring(@searchstring, @index, 100)) -1)

SELECT * FROM Company CO
INNER JOIN Category CA
  ON CA.CategoryId = CO.CategoryId
WHERE CO.[Description] like @A
  or CO.[Description] like @B
  or CO.[Description] like @c
  or CO.[Name] like @A
  or CO.[Name] like @B
  or CO.[Name] like @C
  or CA.[Description] like @A
  or CA.[Description] like @B
  or CA.[Description] like @C

このコードは私には見苦しく見えますが、ユーザーが最大 3 つのアイテムを入力して検索するための要件を満たしているはずです。誰でもそれをきれいにすることについて提案がありますか?

于 2013-09-18T19:00:57.703 に答える
0

(試してみる sql-server インストールがありませんCONTAINS。 を に置き換えて試すことができcolumn LIKE '%string%'ますCONTAINS(column, 'string') )

ここですべてのクエリを参照してください。


別の更新- 他の回答とマニュアルを読んだ後、予想とは異なり、contains 文字列に括弧で囲まれた値は必要ないようです。' | 'したがって、これも機能するはずです-(代わりに試すこともできます' OR '

SELECT CO.name, CA.description FROM company CO
         INNER JOIN category CA
             ON CA.CategoryId = CO.CategoryId
WHERE CONTAINS((CO.name,CO.description), REPLACE('ABC 25 SuperMarket', ' ', ' OR '))
      AND
      CONTAINS(CA.description, REPLACE('ABC 25 SuperMarket', ' ', ' OR '))  

replace の近くで構文エラーが発生する場合は、検索文字列を作成し、それを 2 番目の引数DECLARE @SearchString varchar(MAX) = REPLACE('ABC 25 SuperMarket',' ', ' OR ')の代わりに使用できます。replace(......)


変更された質問に従って更新します-

まず、可能であれば、ロジックをアプリケーション レベルに移動する必要があります。ここで扱うには多すぎると思います。私はこのクエリを思いつきましたが、これは各単語を分割して両方で検索するため、name思っdescriptionたよりも多くの結果が得られることに注意してください。たとえば、これは、前のクエリで名前を持つ1 つだけを返すのと比較して、名前にまたはを含むすべてSupermarketを返します。これは実際に役立つはずです。なぜなら、ユーザーは入力するだけかもしれないからですABC24SupermarketABC 24"ABC Supermarket 24" or "24 ABC Supermarket" or ...

DECLARE @SearchString varchar(MAX) = 'ABC 24 SuperMarket'
DECLARE @separator varchar(MAX) = ' '
DECLARE @Like1 varchar(MAX) = 'CO.name LIKE'
DECLARE @Like2 varchar(MAX) = 'CA.description LIKE'

DECLARE @WHERE1 varchar(MAX) = '( ' + @Like1 + ' ''%' + 
           REPLACE(@SearchString,@separator,'%'' OR ' + @Like1 + ' ''%')+'%'')'

DECLARE @WHERE2 varchar(MAX) = '( ' + @Like2 + ' ''%' + 
           REPLACE(@SearchString,@separator,'%'' OR ' + @Like2 + ' ''%')+'%'')'

DECLARE @QueryString varchar(MAX) =
           CONCAT('SELECT CO.name, CA.description FROM company CO
                       INNER JOIN category CA
                             ON CA.CategoryId = CO.CategoryId
                    WHERE ', @WHERE1, ' AND ', @WHERE2)
exec(@QueryString);

出力@WHERE1すると表示されるはずです

( CO.name LIKE '%ABC%' OR CO.name LIKE '%25%' OR CO.name LIKE '%SuperMarket%')

前に言ったように、次CONTAINSのような括弧で囲まれた値を使用してみてください。

DECLARE @SearchString varchar(MAX) = 'ABC 25 SuperMarket'
DECLARE @separator varchar(MAX) = ' '

DECLARE @WHEREString varchar(MAX) = '''"' + 
        REPLACE(@SearchString, @separator, '" OR "')+'"'''

SELECT CO.name, CA.description FROM company CO
        INNER JOIN category CA
           ON CA.CategoryId = CO.CategoryId
WHERE CONTAINS((CO.name,CO.description), @WHEREString)
      AND 
      CONTAINS(CA.description, @WHEREString)

出力@WHEREStringすると表示されるはずです

'"ABC" OR "25" OR "SuperMarket"'

以前の回答:

descriptionこれは、最後のスペースの後の単語が theで、残りが「名前」であると想定します。

以下に示すように、検索文字列を分割して使用できます。like私はSQLサーバーをインストールしていないので、このクエリは使用しています。

DECLARE @SearchString VARCHAR(100) = 'ABC 24 Supermarket'
DECLARE @searchLength int = len(@SearchString)
DECLARE @searchReverse VARCHAR(100) = reverse(@SearchString)

SELECT CO.name, CA.description FROM company CO
        INNER JOIN category CA
            ON CA.CategoryId = CO.CategoryId
WHERE CO.name LIKE concat( '%', SUBSTRING(@SearchString,0,@searchLength-charindex(' ',@searchReverse)+1), '%')
     AND 
     CA.description LIKE concat( '%', SUBSTRING(@SearchString,@searchLength-charindex(' ',@searchReverse)+2,@searchLength), '%')

これはうまくいくはずです。where 句は OR ではなく AND を使用していることに注意してください。

DECLARE @SearchString VARCHAR(100) = 'ABC 24 Supermarket'
DECLARE @searchLength int = len(@SearchString)
DECLARE @searchReverse VARCHAR(100) = reverse(@SearchString)
DECLARE @company VARCHAR(100) = SUBSTRING(@SearchString,0,@searchLength-charindex(' ',@searchReverse)+1)
DECLARE @category VARCHAR(100) = SUBSTRING(@SearchString,@searchLength-charindex(' ',@searchReverse)+2,@searchLength)
    
SELECT CO.name, CA.description FROM company CO
        INNER JOIN category CA
           ON CA.CategoryId = CO.CategoryId
WHERE CONTAINS((CO.name, CO.description), @company)
      AND
      CONTAINS(CA.description , @category)
于 2013-09-08T19:27:33.133 に答える
-1

CONTAIN の代わりに FREETEXT を使用できます。

DECLARE @SearchString VARCHAR(100) = '"ABC 24 Supermarket"'
SELECT * FROM Company CO
INNER JOIN Category CA
ON CA.CategoryId = CO.CategoryId
WHERE FREETEXT((CO.[Description], CO.[Name]), @SearchString)
OR FREETEXT(CA.[Description], @SearchString)
于 2013-09-17T08:17:44.613 に答える