Существуют две таблицы, связанные идентификатором:
item_tbl (id)
link_tbl (item_id)
В item_tbl
есть записи, которые не имеют соответствующих строк в link_tbl
. Выбор, который будет считать их количество, будет:
SELECT COUNT(*)
FROM link_tbl lnk LEFT JOIN item_tbl itm ON lnk.item_id=itm.id
WHERE itm.id IS NULL
Я хотел бы удалить те сиротские записи (те, которые не имеют соответствия в другой таблице) из link_tbl
, но единственный способ, о котором я мог думать, был:
DELETE FROM link_tbl lnk
WHERE lnk.item_id NOT IN (SELECT itm.id FROM item_tbl itm)
Есть
262,086,253 записей в link_tbl
3,033,811 в item_tbl
16 844 347 сиротских записей в link_tbl
.
Сервер имеет 4 ГБ оперативной памяти и 8-ядерный процессор.
EXPLAIN DELETE FROM link_tbl lnk
WHERE lnk.item_id NOT IN (SELECT itm.id FROM item_tbl itm)
Возврат:
Delete on link lnk (cost=0.00..11395249378057.98 rows=131045918 width=6)
-> Seq Scan on link lnk (cost=0.00..11395249378057.98 rows=131045918 width=6)
Filter: (NOT (SubPlan 1))
SubPlan 1
-> Materialize (cost=0.00..79298.10 rows=3063207 width=4)
-> Seq Scan on item itm (cost=0.00..52016.07 rows=3063207 width=4)
Вопросы:
- Есть ли лучший способ удаления сиротских записей из
link_tbl
? -
Насколько точным является объяснение выше, или как долго это может потребоваться для удаления этих записей?
- Изменить: исправлено в соответствии с комментарием Erwin Brandstetter.
- Изменить: версия PostgreSql - 9.1
- Изменить: некоторые части postgresql.config
- shared_buffers = 368MB
- temp_buffers = 32MB
- work_mem = 32MB
- maintenance_work_mem = 64MB
- max_stack_depth = 6MB
- fsync = off
- synchronous_commit = off
- full_page_writes = off
- wal_buffers = 16MB
- wal_writer_delay = 5000ms
- commit_delay = 10
- commit_siblings = 10
- effective_cache_size = 1600MB
- Изменить: исправлено в соответствии с комментарием Erwin Brandstetter.
Разрешение:
Спасибо всем за ваши советы, это было очень полезно. Я, наконец, использовал удаление, рекомендованное Erwin Brandstetter qaru.site/info/407988/..., но немного изменил его:
DELETE FROM link_tbl lnk
WHERE lnk.item_id BETWEEN 0 AND 10000
AND lnk.item_id NOT IN (SELECT itm.id FROM item itm
WHERE itm.id BETWEEN 0 AND 10000)
Я сравнивал результаты для NOT IN и NOT EXISTS, а результат ниже, хотя я использовал COUNT вместо DELETE, который, я думаю, должен быть таким же (я имею в виду для сравнения):
EXPLAIN ANALYZE SELECT COUNT(*)
FROM link_tbl lnk
WHERE lnk.item_id BETWEEN 0 AND 20000
AND lnk.item_id NOT IN (SELECT itm.id
FROM item_tbl itm
WHERE itm.id BETWEEN 0 AND 20000);
QUERY PLAN
Aggregate (cost=6002667.56..6002667.57 rows=1 width=0) (actual time=226817.086..226817.088 rows=1 loops=1)
-> Seq Scan on link_tbl lnk (cost=1592.50..5747898.65 rows=101907564 width=0) (actual time=206.029..225289.570 rows=566625 loops=1)
Filter: ((item_id >= 0) AND (item_id <= 20000) AND (NOT (hashed SubPlan 1)))
SubPlan 1
-> Index Scan using item_tbl_pkey on item_tbl itm (cost=0.00..1501.95 rows=36221 width=4) (actual time=0.056..99.266 rows=17560 loops=1)
Index Cond: ((id >= 0) AND (id <= 20000))
Total runtime: 226817.211 ms
EXPLAIN ANALYZE SELECT COUNT(*)
FROM link_tbl lnk WHERE lnk.item_id>0 AND lnk.item_id<20000
AND NOT EXISTS (SELECT 1 FROM item_tbl itm WHERE itm.id=lnk.item_id);
QUERY PLAN
Aggregate (cost=8835772.00..8835772.01 rows=1 width=0)
(actual time=1209235.133..1209235.135 rows=1 loops=1)
-> Hash Anti Join (cost=102272.16..8835771.99 rows=1 width=0)
(actual time=19315.170..1207900.612 rows=566534 loops=1)
Hash Cond: (lnk.item_id = itm.id)
-> Seq Scan on link_tbl lnk (cost=0.00..5091076.55 rows=203815128 width=4) (actual time=0.016..599147.604 rows=200301872 loops=1)
Filter: ((item_id > 0) AND (item_id < 20000))
-> Hash (cost=52016.07..52016.07 rows=3063207 width=4) (actual time=19313.976..19313.976 rows=3033811 loops=1)
Buckets: 131072 Batches: 4 Memory Usage: 26672kB
-> Seq Scan on item_tbl itm (cost=0.00..52016.07 rows=3063207 width=4) (actual time=0.013..9274.158 rows=3033811 loops=1)
Total runtime: 1209260.228 ms
NOT EXISTS был в 5 раз медленнее.
Фактическое удаление данных не заходило так долго, как я волновался, я смог удалить его в 5 партий (10000-20000,20000-100000,100000-200000,200000-1000000 и 1000000-1755441), Сначала я обнаружил max item_id, и мне оставалось только пройти половину таблицы.
Когда я пробовал NOT IN или EXISTS без диапазона (с подсчетом выбора), он даже не закончил, я разрешил ему работать в течение ночи, и он все еще работал утром.
Я думаю, что я искал DELETE с использованием ответа от wildplasser qaru.site/info/407988/..., но он пришел слишком поздно.
DELETE FROM one o
USING (
SELECT o2.id
FROM one o2
LEFT JOIN two t ON t.one_id = o2.id
WHERE t.one_id IS NULL
) sq
WHERE sq.id = o.id
;