1

セット内のすべての要素が列内の行の一部に含まれているかどうかを確認したい。例えば、

セット A = {5013、aab、402dha)

B列

adaf**5013**dad344  
23**aab**yyyy  
zzz**402dha**vuuuda  
.....  
...
  1. 先頭と末尾の文字があるため、「A の列 B の場所」は使用できません。
  2. 検索したい要素が列行のどの部分にもある可能性があるため、TRIM、SUBSTRING は使用できません。
  3. A の要素が 3 つしかない場合、where 句に 3 つの正規表現を記述できますが、A の要素が ~1000 の場合はそれができません。

私の質問は、それを行う方法があるかどうかです。それともエクセルの「VLookup」に似た機能はありますか?

どんなアイデアでも大歓迎です!

4

1 に答える 1

5

これはかなり恐ろしいスキーマです。複数の値を次の場所に保存するように変更すると、最良の結果が得られます。

  • 通常のリレーショナル子テーブル。
  • 配列; また
  • hstore

これらはすべて、非常に単純で健全な SQL 式を使用して必要なものを決定することを可能にし、大きなテーブルでのパフォーマンスを向上させるために (子テーブルの通常の b ツリー インデックスを介して、配列と hstore の GiST または GIN インデックスを介して) インデックス付け可能です。 .

そのままでももちろん可能ですが、パフォーマンスは悲惨なものになります。1 つの方法は、 を使用regexp_split_to_arrayして列を配列に変換してから、配列演算子を使用してオーバーラップをテストすることです。

この SQLFiddle デモを参照してください。このデモでは、拡張テスト セットを使用しています。これは、問題を実証するのに十分ではなかったためです。

「いずれかのセットが列に表示されます」( %%) と「すべてのセットが列に表示されます」 ( ) の両方を示しまし@>た。


設定:

CREATE TABLE test(gah text);

INSERT INTO test(gah) VALUES
('adaf**5013**dad344'),
('23**aab**yyyy'),
('zzz**402dha**vuuuda'),
('no**matches**here**lalala'),
('5013**aab**402dha'),
('402dha**aab**somethingelse**5013'),
('402dha**aab**5013');

デモ:

regress=> SELECT gah FROM test 
          WHERE regexp_split_to_array(gah, '\*\*') && ARRAY['5013', 'aab', '402dha'];
               gah                
----------------------------------
 adaf**5013**dad344
 23**aab**yyyy
 zzz**402dha**vuuuda
 5013**aab**402dha
 402dha**aab**somethingelse**5013
 402dha**aab**5013
(6 rows)

regress=> SELECT gah FROM test 
          WHERE regexp_split_to_array(gah, '\*\*') @> ARRAY['5013', 'aab', '402dha'];
               gah                
----------------------------------
 5013**aab**402dha
 402dha**aab**somethingelse**5013
 402dha**aab**5013
(3 rows)

驚くべきことに、PostgreSQL の式インデックスのサポートを利用することで、このクエリに役立つインデックスを実際に作成できます。もちろん、それができるからといって、それが良いアイデアであるとは限りません。

regress=> CREATE INDEX test_glah_resplit_gin ON test 
          USING GIN(( regexp_split_to_array(gah, '\*\*') ));
CREATE INDEX
regress=> -- Only for testing purposes, don't use in production:
regress=> SET enable_seqscan = off;
SET
regress=> explain SELECT gah FROM test WHERE regexp_split_to_array(gah, '\*\*') @> ARRAY['5013', 'aab', '402dha'];
                                          QUERY PLAN                                           
-----------------------------------------------------------------------------------------------
 Bitmap Heap Scan on test  (cost=16.00..20.02 rows=1 width=32)
   Recheck Cond: (regexp_split_to_array(gah, '\*\*'::text) @> '{5013,aab,402dha}'::text[])
   ->  Bitmap Index Scan on test_glah_resplit_gin  (cost=0.00..16.00 rows=1 width=0)
         Index Cond: (regexp_split_to_array(gah, '\*\*'::text) @> '{5013,aab,402dha}'::text[])
(4 rows)

regress=> explain SELECT gah FROM test WHERE regexp_split_to_array(gah, '\*\*') && ARRAY['5013', 'aab', '402dha'];
                                          QUERY PLAN                                           
-----------------------------------------------------------------------------------------------
 Bitmap Heap Scan on test  (cost=16.00..20.02 rows=1 width=32)
   Recheck Cond: (regexp_split_to_array(gah, '\*\*'::text) && '{5013,aab,402dha}'::text[])
   ->  Bitmap Index Scan on test_glah_resplit_gin  (cost=0.00..16.00 rows=1 width=0)
         Index Cond: (regexp_split_to_array(gah, '\*\*'::text) && '{5013,aab,402dha}'::text[])
(4 rows)

GIN インデックスは更新に費用がかかるため、このアプローチを使用すると、 insert/に対してかなりのパフォーマンスの代償を払うことになります。updateこれは通常の配列にも当てはまります。その場でそれらを作成するために使用regexp_split_to_tableすると、少し悪化します。GIN のヒントGIN インデックスの概要を参照してください。

たとえば、テスト テーブルに 100 万行を挿入するINSERT INTO test(gah) SELECT 'aaaaabbbbb'||(x::text) FROM generate_series(1,1000000) x;と、GIN インデックスが配置されている場合は 22 秒、ドロップした場合は 1.6 秒かかりました。ただし、値が均一であるため、これは特に悪いケースになる可能性があります。

于 2012-11-14T22:53:55.677 に答える