web-dev-qa-db-ja.com

同じ名前の列のUPDATEIDテーブル

私は初心者の質問かもしれませんが、これを解決する方法がわかりません。私は次のようなテーブルを持っています:

name | id       | value
A      1286487    1286333
B      1286489    1286403
C      1286495    1286455
C      1286496    1286375
D      1286503    1286341
B      1286506    1286343

そして、このテーブルを次のように更新したいと思います。

name | id       | value
A      1286487    1286333
B      1286489    1286403
C      1286495    1286455
C      1286495    1286375
D      1286503    1286341
B      1286489    1286343

したがって、名前BとCの行は、この名前の最初の行と同じIDを持ちます。誰かがそれを手伝ってくれる?

1
Bilbo Bagosz

要件を満たすには、最初に必要なIDを見つける必要があります。これは、当然のことながら、min()関数を使用して見つけることができます。次に、更新を行います。最も速く作成できるのは、おそらく次のとおりです。

_UPDATE minupdate m 
   SET id = (SELECT min(id) 
               FROM minupdate
              WHERE name = m.name);
_

これの裏側は、行数が多い場合、非常に遅くなる可能性があることです。次のステートメントを使用して、テーブルに約50,000のランダムな行を入力しました。

_INSERT INTO minupdate (name, id)
SELECT translate(substr(i::text, 1, 1), '1234567890', 'ABCDEFGHIJ'), 
       random() * 100000
FROM generate_series(1, 50000) t(i);
_

私のテストボックスでは、インデックスなしで、これには非常に長い時間がかかりました-実際の実行プランが生成されるのを待ちませんでした(EXPLAIN (ANAYLZE, BUFFERS)を使用しましたが、キャンセルしてEXPLAINで実行して確認しましたどうしましたか:

_ Update on minupdate m  (cost=0.00..136044281.50 rows=87720 width=12)
   ->  Seq Scan on minupdate m  (cost=0.00..136044281.50 rows=87720 width=12)
         SubPlan 1
           ->  Aggregate  (cost=1550.87..1550.88 rows=1 width=4)
                 ->  Seq Scan on minupdate  (cost=0.00..1526.50 rows=9747 width=4)
                       Filter: (name = m.name)
_

そこで、(name、id)にインデックスを追加して、より高速な結果を取得しました。

_ Update on minupdate m  (cost=0.00..417486.00 rows=50000 width=15) (actual time=1771.008..1771.008 rows=0 loops=1)
   Buffers: shared hit=394845 read=653 dirtied=654
   ->  Seq Scan on minupdate m  (cost=0.00..417486.00 rows=50000 width=15) (actual time=0.062..1317.366 rows=50000 loops=1)
         Buffers: shared hit=172641 read=192
         SubPlan 2
           ->  Result  (cost=8.31..8.32 rows=1 width=0) (actual time=0.018..0.020 rows=1 loops=50000)
                 Buffers: shared hit=171655 read=192
                 InitPlan 1 (returns $1)
                   ->  Limit  (cost=0.29..8.31 rows=1 width=4) (actual time=0.011..0.012 rows=1 loops=50000)
                         Buffers: shared hit=171655 read=192
                         ->  Index Only Scan using minupdate_name_id_idx on minupdate  (cost=0.29..8.31 rows=1 width=4) (actual time=0.006..0.006 rows=1 loops=50000)
                               Index Cond: ((name = m.name) AND (id IS NOT NULL))
                               Heap Fetches: 50000
                               Buffers: shared hit=171655 read=192
_

ここで問題はこれです:計画の内部は50,000回実行されます-更新される行の数。インデックスのないバージョンでは、テーブル全体をスキャンすることがありますが、非常に時間がかかるのも不思議ではありません。

しかし、それだけではありません。それらの50,000ループは醜いです-もっと良いものを見つけましょう:

_EXPLAIN (ANALYZE, BUFFERS)
UPDATE minupdate m 
   SET id = mins.id 
  FROM (SELECT name, min(id) AS id 
          FROM minupdate 
         GROUP BY name
       ) mins
WHERE m.name = mins.name;
_

これにより、より合理化された計画が得られます。

_ Update on minupdate m  (cost=1861.76..4525.59 rows=91698 width=42) (actual time=995.845..995.845 rows=0 loops=1)
   Buffers: shared hit=241809 read=394 dirtied=843
   ->  Hash Join  (cost=1861.76..4525.59 rows=91698 width=42) (actual time=218.866..542.437 rows=50000 loops=1)
         Hash Cond: (m.name = mins.name)
         Buffers: shared hit=972 dirtied=427
         ->  Seq Scan on minupdate m  (cost=0.00..1402.98 rows=91698 width=8) (actual time=0.027..106.868 rows=50000 loops=1)
               Buffers: shared hit=486 dirtied=1
         ->  Hash  (cost=1861.65..1861.65 rows=9 width=36) (actual time=218.816..218.816 rows=9 loops=1)
               Buckets: 1024  Batches: 1  Memory Usage: 1kB
               Buffers: shared hit=486 dirtied=426
               ->  Subquery Scan on mins  (cost=1861.47..1861.65 rows=9 width=36) (actual time=218.738..218.790 rows=9 loops=1)
                     Buffers: shared hit=486 dirtied=426
                     ->  HashAggregate  (cost=1861.47..1861.56 rows=9 width=6) (actual time=218.722..218.740 rows=9 loops=1)
                           Group Key: minupdate.name
                           Buffers: shared hit=486 dirtied=426
                           ->  Seq Scan on minupdate  (cost=0.00..1402.98 rows=91698 width=6) (actual time=0.007..111.580 rows=50000 loops=1)
                                 Buffers: shared hit=486 dirtied=426
 Planning time: 0.164 ms
 Execution time: 995.908 ms
_

現在、そこには2つの順次スキャンノードがありますが(やや意外なことに)、両方とも1回だけ実行されます(_loops=1_)。

ここで行ったことは、相関サブクエリを削除し、一時ビュー(FROMの後のクエリ)に置き換えて、更新するテーブルに結合することです。このアプローチは、多くの場合、簡単な方法よりもはるかに優れた計画を提供します。大きなテーブルでは、パフォーマンスの違いが非常に大きくなる可能性があります。

4
dezso