5

Postgresql のフィールド内の単語数で大きなテーブル (約 1M 行) を更新しようとしています。このクエリは機能し、テーブルtoken_count内の単語 (トークン) をカウントするフィールドを設定します。longtextmy_table

UPDATE my_table mt SET token_count = 
    (select count(token) from 
      (select unnest(regexp_matches(t.longtext, E'\\w+','g')) as token
      from my_table as t where mt.myid = t.myid)
    as tokens);

myidテーブルの主キーです。 \\w+特殊文字を無視して単語を数えたいので必要です。たとえば、A test . ; )スペースベースのカウントでは 5 を返しますが、正しい値は 2 です。問題は、それが恐ろしく遅く、1M 行で完了するのに 2 日では足りないことです。それを最適化するために何をしますか?結合を回避する方法はありますか?

たとえばlimitandを使用して、バッチをブロックに分割するにはどうすればよいoffsetですか?

ヒントをありがとう、

ムロン

更新: array_split のパフォーマンスを測定しましたが、とにかく更新が遅くなります。したがって、解決策はそれを並列化することで構成されるかもしれません。から別のクエリを実行するとpsql、1 つのクエリだけが機能し、他のクエリはそのクエリが完了するまで待機します。更新を並列化するにはどうすればよいですか?

4

4 に答える 4

11

使ってみましたarray_lengthか?

UPDATE my_table mt
SET token_count = array_length(regexp_split_to_array(trim(longtext), E'\\W+','g'), 1)

http://www.postgresql.org/docs/current/static/functions-array.html

# select array_length(regexp_split_to_array(trim(' some long text  '), E'\\W+'), 1);
 array_length 
--------------
            3
(1 row)
于 2013-06-19T18:00:53.790 に答える
2
UPDATE my_table
SET token_count = array_length(regexp_split_to_array(longtext, E'\\s+'), 1)

または、相関関係のない元のクエリ

UPDATE my_table
SET token_count = (
    select count(*)
    from (select unnest(regexp_matches(longtext, E'\\w+','g'))) s
    );
于 2013-06-19T18:12:06.677 に答える
-2
  1. myidインデックスの最初のフィールドであることを確認してください。

  2. そもそもこれをDBの外で行うことを検討してください。ベンチマークなしで言うのは難しいですが、カウントは選択+更新よりもコストがかかる場合があります。だから価値があるかもしれません。

    • COPY コマンド (Postgres の BCP に相当) を使用して、テーブル データをまとめてファイルに効率的にコピーします。

    • 簡単な Perl スクリプトを実行してカウントします。Perl の場合、IO の速度にもよりますが、100 万行には数分から 1 時間かかります。

    • COPY を使用してテーブルを DB にコピーします (おそらく一時テーブルにコピーしてから、その一時テーブルから更新します。または、ダウンタイムを許容できる場合は、メイン テーブルを切り捨てて、そこに直接 COPY することをお勧めします)。

  3. あなたのアプローチと私のアプローチ#2の最後のステップの両方で、5000行のバッチでtoken_countを更新します(たとえば、rowcountを5000に設定where token_count IS NULLし、クエリに追加する更新をループします

于 2013-06-19T17:59:02.897 に答える