web-dev-qa-db-ja.com

別のテーブルの列の一致する単語に対する結合条件を使用したUPDATE

次のような2つのテーブルがあります。

表A:

CREATE TEMP TABLE table_a (
  Column_1 text,
  ID_number int
);
INSERT INTO table_a VALUES
  ('foo,bar,baz', 123),
  ('qux,quux,quuz',456),
  ('corge,grault,garply',789),
  ('qux,bar,grault', 101);

表B:

CREATE TEMP TABLE table_b (
  Column_1 text,
  Column_2 text,
  ID_number int
);
INSERT INTO table_b VALUES
  ('foo','baz',null),
  ('qux','quzz',null),
  ('corge','garply',null);

テーブルAの列1と2の値がテーブルAの列1の同じ行にあるテーブルAのID_number列から値をコピーしようとしています。

これは私が考えていたようなものです:

UPDATE table_b AS B 
SET id_number = A.id_number 
FROM table_a AS A 
WHERE A.column_1 LIKE B.column_1
  AND A.column_1 LIKE B.column_2

..しかし、明らかにこれは機能しません。

これを適切なクエリに変換するにはどうすればよいですか?

追加情報

table_a.Column_1には、英国の住所が含まれています。次に例を示します。

'47 BOWERS PLACE, GREAT YARMOUTH, NORFOLK, NR20 4AN'

table_bには、Column_1に住所の最初の行('47 BOWERS PLACE')があり、'NR20 4AN'に郵便番号(Column_2)があります。

物事を単純化するのが最善だと思いましたが、実際のデータはこの状況である程度の関連性があります。

table_aには約3,000万のアドレスがあります。 table_bには約60k行あります。

パフォーマンスは重要であり、これが高速で実行されるほど優れており、今後も繰り返される可能性があります。

5
Matt

仮定Postgres 9.6、パフォーマンスに関連、大きなテーブル、文字で構成される「単語」、空白なしまたは句読点、ステミングまたはストップワードなし、フレーズなし、すべての列NOT NULL

全文検索 インデックスに裏打ちされたものは、最速のソリューションの1つです:

UPDATE table_b b
SET    id_number = a.id_number 
FROM   table_a a
WHERE  to_tsvector('simple', a.column_1)
    @@ plainto_tsquery('simple', concat_ws(' ', b.column_1, b.column_2))
AND    b.id_number = a.id_number;  -- prevent empty UPDATEs

a.column_1に一致する式のインデックスがある場合:

CREATE INDEX table_a_column_1_idx ON table_a USING GIN (to_tsvector('simple', column_1));
2

ここで重要なのは、_Column_1_がJOINの3つの可能な値を表すことです。したがって、使用したいのは string_to_array() です(これらの値がコンマで区切られていて、それ自体にコンマを含めることができない場合)。

このクエリを実行し、

_SELECT id_number, string_to_array(column_1, ',') AS column_1
FROM table_a;
 id_number |       column_1        
-----------+-----------------------
       123 | {foo,bar,baz}
       456 | {qux,quux,quuz}
       789 | {corge,grault,garply}
       101 | {qux,bar,grault}
_

これで、_ = ANY() を使用してUPDATEを実行できます。

_UPDATE table_b
SET id_number = A.id_number
FROM (
  SELECT id_number, string_to_array(column_1, ',') AS column_1
  FROM table_a
) AS A
WHERE table_b.column_1 = ANY(A.column_1)
  AND table_b.column_2 = ANY(A.column_1);
_

または、 _<@_ を使用することもできます

_WHERE ARRAY[table_b.column_1, table_b.column_2] <@ A.column_1;
_

これにより、さらにコンパクトになります。

_UPDATE table_b
  SET id_number = A.id_number
FROM table_a AS A                              
  WHERE ARRAY[table_b.column_1, table_b.column_2] <@ string_to_array(A.column_1, ',');
_
6
Evan Carroll

これを試して:

update table_b
set id_number = (select id_number
                 from table_a
                 where table_a.Column_1 like '%' || table_b.Column_1 || '%'
                       OR table_a.Column_1 like '%' || table_b.Column_2 || '%'
                 limit 1)
;

Column_1を配列に変換することで別の解決策になる可能性もありますが、これは非常に明確です。

テキストがTable_Aの複数のcolumn_1に表示される場合に備えて、検索を1レコードに制限していることに注意してください。

Evan Carroll がコメントセクションで指摘したように、このコードはテーブル全体を更新することに注意します。

ここで確認してください: http://rextester.com/MUL459

4
McNets