私は初心者の質問かもしれませんが、これを解決する方法がわかりません。私は次のようなテーブルを持っています:
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を持ちます。誰かがそれを手伝ってくれる?
要件を満たすには、最初に必要な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
の後のクエリ)に置き換えて、更新するテーブルに結合することです。このアプローチは、多くの場合、簡単な方法よりもはるかに優れた計画を提供します。大きなテーブルでは、パフォーマンスの違いが非常に大きくなる可能性があります。