3

文字列 (TEXT) でいっぱいのテーブルがあり、同じテーブル内の他の文字列の部分文字列であるすべての文字列を取得したいと考えています。たとえば、テーブルに次の 3 つの文字列があるとします。

WORD        WORD_ID
cup         0
cake        1
cupcake     2

私のクエリの結果として、次のようなものを取得したいと思います。

WORD        WORD_ID        SUBSTRING        SUBSTRING_ID
cupcake     2              cup              0
cupcake     2              cake             1 

テーブル内のすべての単語をループし、同じテーブル内のすべての単語と照合することで、2 つのループ (Python または JS を使用) でこれを実行できることはわかっていますが、SQL (PostgreSQLそのことについては)。

4

3 に答える 3

3

自己結合を使用します。

select w1.word, w1.word_id, w2.word, w2.word_id
from words w1
join words w2
on w1.word <> w2.word
and w1.word like format('%%%s%%', w2.word);

  word   | word_id | word | word_id 
---------+---------+------+---------
 cupcake |       2 | cup  |       0
 cupcake |       2 | cake |       1
(2 rows)
于 2015-11-13T23:21:27.820 に答える
1

問題

このタスクは、インデックスを利用できない限りO(N²)問題であるため、重要なサイズのテーブルに対してデータベース サーバーを停止させる可能性があります。

シーケンシャル スキャンでは、2 つの行のすべての可能な組み合わせ、つまり組み合わせをチェックする必要があります。逆の重複した組み合わせを除外するのは簡単ではないため、n * (n-1) / 2Postgres はテストを実行します。n * n-1最初の試合に満足すれば、安くなります - いくらになるかは、データの配布によって異なります。多くの一致の場合、Postgres は行の一致を早期に見つけ、残りのテストをスキップできます。いくつかの一致については、ほとんどのチェックをとにかく実行する必要があります。

いずれにせよ、パフォーマンスはテーブル内の行数とともに急速に低下します。EXPLAIN ANALYZEテーブル内の 10、100、1000 などの行で各クエリをテストして、自分で確認してください。

解決

できればGINにトライグラム インデックスを作成します。word

CREATE INDEX tbl_word_trgm_gin_idx ON tbl USING gin (word gin_trgm_ops);

詳細:

これまでの両方の回答のクエリは、インデックスがあったとしてもそれを使用しません。このインデックスで実際に機能するクエリを使用します。

すべての一致を一覧表示するには (質問の本文に従って): 次
を使用しLATERAL CROSS JOINます。

SELECT t2.word_id, t2.word, t1.word_id, t1.word
FROM   tbl t1
     , LATERAL (
   SELECT word_id, word
   FROM   tbl
   WHERE  word_id <> t1.word_id
   AND    word like format('%%%s%%', t1.word)
   ) t2;

(タイトルに従って)一致する行を取得するEXISTSには:半結合を使用します:

SELECT t1.word_id, t1.word
FROM   tbl t1
WHERE EXISTS (
   SELECT 1
   FROM   tbl
   WHERE  word_id <> t1.word_id
   AND    word like format('%%%s%%', t1.word)
   );
于 2015-11-14T03:13:38.070 に答える
0

私はこれに次のようにアプローチします:

select w1.word_id, w1.word, w2.word_id as substring_id w2.word as substring
from words w1 join
     words w2
     on w1.word like '%' || w2.word || '%' and w1.word <> w2.word;

注: これは、アプリケーションでループを実行するよりもおそらく少し高速です。ただし、このクエリは Postgres でネストされたループとして実装されるため、非常に高速ではありません。

于 2015-11-13T23:25:33.773 に答える