「LIKE OR LIKE、OR LIKE、OR LIKE、OR LIKE」のより良いアプローチ


10

この質問では、彼は私と同じ問題を抱えています。私は次のようなものが必要です:

select * from blablabla 
where product 
like '%rock%' or
like '%paper%' or
like '%scisor%' or
like '%car%' or
like '%pasta%' 

これは醜く、インデックスを使用していません。この場合、これは実際にこれを行う唯一の方法です(文字列内の複数の単語を選択するため)、またはFULLTEXTを使用する必要がありますか?

私が理解しているように、フルテキストでは、文字列内の複数の単語を選択できます。

この質問は全文についても話します


3
製品列のデータ型は何ですか?平均で何文字?
ジョー・オブビッシュ2017

回答:


17

フルテキストインデックスは通常、特効薬ではなく、追加のメンテナンス、ディスク容量、およびクエリパターンへのかなり煩わしい変更が必要です。

大きなドキュメント(メール本文、PDF、Word文書など)にインデックスを付ける必要がない限り、それらはやりすぎです(そして、正直なところ、SQL Serverからそのプロセスを完全に取り除きます。 Elasticsearchなどを使用します)。

ユースケースが小さい場合は、一般に計算カラムがより優れたアプローチです。

これは簡単なデモ設定です:

use tempdb

CREATE TABLE #fulltextindexesarestupid (Id INT PRIMARY KEY CLUSTERED, StopAbusingFeatures VARCHAR(100))

INSERT #fulltextindexesarestupid (Id)
SELECT TOP 1000000 ROW_NUMBER() OVER (ORDER BY (@@ROWCOUNT))
FROM sys.messages AS m
CROSS JOIN sys.messages AS m2

UPDATE #fulltextindexesarestupid
SET StopAbusingFeatures = CASE WHEN Id % 15 = 0 THEN 'Bad'
                               WHEN Id % 3 = 0 THEN 'Idea'
                               WHEN Id % 5 = 0 THEN 'Jeans'
                               END


ALTER TABLE #fulltextindexesarestupid 
ADD LessBad AS CONVERT(BIT, CASE WHEN StopAbusingFeatures LIKE '%Bad%' THEN 1
                    WHEN StopAbusingFeatures LIKE '%Idea%' THEN 1
                    ELSE 0 END)

CREATE UNIQUE NONCLUSTERED INDEX ix_whatever ON #fulltextindexesarestupid (LessBad, Id)

非永続列にも基づいてクエリを実行すると、「インデックスを使用する」計画とすべてが得られます:)

SELECT COUNT(*)
FROM #fulltextindexesarestupid AS f
WHERE LessBad = 1

ナッツ


-3

sp_BlitzErikの答えには多くの良い点がありますが、それがフルテキスト検索を使用すべきではない理由だとは思いません。全文検索は、あなたが思っていることを実行するためのものではありません。複数のフィールドを検索するためのものではありません。単語の内容をベクトル化し、辞書、スタブ、字句解析器、地名辞典、ストップワードの除去、および他に適用されない多くのトリックを利用するためにあります。または、まだ適用されることが示されていません。

SQL Serverでこれをより適切に行う方法はわかりませんが、解決策にも同意しません。PostgreSQL用に彼のデータを再作成してみましょう-PostgreSQLで作成する方がずっとクリーンです。

CREATE TABLE fulltextindexesarestupid
AS
  SELECT
    id,
    CASE WHEN Id % 15 = 0 THEN 'Bad'
      WHEN Id % 3 = 0 THEN 'Idea'
      WHEN Id % 5 = 0 THEN 'Jeans'
    END AS StopAbusingFeatures
  FROM generate_series(1,1000000) AS id;

ここで必要なのは列挙型です。

CREATE TYPE foo AS ENUM ('Bad', 'Idea', 'Jeans');

ALTER TABLE fulltextindexesarestupid
  ALTER StopAbusingFeatures
  SET DATA TYPE foo
  USING StopAbusingFeatures::foo;

これで、文字列を整数表現に折りたたみました。しかし、以前と同じようにクエリを実行できます。

SELECT *
FROM fulltextindexesarestupid
WHERE StopAbusingFeatures = 'Bad';

これには効果があります。

  1. カテゴリが列挙型であることを隠します。その複雑さはタイプにカプセル化され、ユーザーから隠されます。
  2. また、タイプのこれらのカテゴリにメンテナンスを配置します。
  3. それは標準化されています。
  4. 行サイズは大きくなりません。

これらの利点がなければ、基本的には文字列比較を最適化しようとするだけです。しかし、悲しいかな、提案のコードを考えると、sp_BlitzErikがどのようにして答えに到達するかさえわかりません。

like '%rock%' or
like '%paper%' or
like '%scisor%' or
like '%car%' or
like '%pasta%'

enum、またはsp_BlitzErikによって提案されたハンドローリングメソッドを使用して、トークンを整数に折りたたむことができますが、折りたたむことができる場合は、なぜアンアンカーのように行うのですか?つまり、「%pasta%」がトークン「pasta」であることを知っている場合、なぜ%その両側にがあるのでしょうか。'%'がないと、これは等価チェックであり、テキストとしてもかなり高速になります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.