У меня есть очень простой запрос, сгенерированный моей ORM (Entity Framework Core):
SELECT *
FROM "table1" AS "t1"
WHERE EXISTS (
SELECT 1
FROM "table2" AS "t2"
WHERE ("t2"."is_active" = TRUE) AND ("t1"."table2_id" = "t2"."id"))
ORDER BY "t1"."table2_id"
- Есть 2 записи is_active. Другие задействованные столбцы («id») являются первичными ключами. Запрос возвращает ровно 4 строки.
- Таблица 1 составляет 96 миллионов записей.
- Таблица 2 составляет 30 миллионов записей.
- 3 столбца, участвующие в этом запросе, индексируются (is_active, id, table2_id).
- Код C#/LINQ, создающий этот простой запрос, выглядит следующим образом: Table2.Where(t => t.IsActive).Include(t => t.Table1).ToList();`
SET STATISTICS 10000
было установлено для всех трех столбцов.VACUUM FULL ANALYZE
был запущен на обеих таблицах.
БЕЗ предложения ORDER BY
запрос возвращается в течение нескольких миллисекунд, и я не ожидаю ничего другого для возврата 4 записей. ОБЪЯСНИТЕ вывод:
Nested Loop (cost=1.13..13.42 rows=103961024 width=121)
-> Index Scan using table2_is_active_idx on table2 (cost=0.56..4.58 rows=1 width=8)
Index Cond: (is_active = true)
Filter: is_active
-> Index Scan using table1_table2_id_fkey on table1 t1 (cost=0.57..8.74 rows=10 width=121)
Index Cond: (table2_id = table1.id)
С предложением ORDER BY
выполнение запроса занимает 5 минут! ОБЪЯСНИТЕ вывод:
Merge Semi Join (cost=10.95..4822984.67 rows=103961040 width=121)
Merge Cond: (t1.table2_id = t2.id)
-> Index Scan using table1_table2_id_fkey on table1 t1 (cost=0.57..4563070.61 rows=103961040 width=121)
-> Sort (cost=4.59..4.59 rows=2 width=8)
Sort Key: t2.id
-> Index Scan using table2_is_active_idx on table2 a (cost=0.56..4.58 rows=2 width=8)
Index Cond: (is_active = true)
Filter: is_active
Внутреннее, первое сканирование индекса должно возвращать не более 2 строк. Тогда внешнее, второе сканирование индекса не имеет смысла с его стоимостью 4563070 и 103961040 строк. Он должен сопоставить только 2 строки в table2
с 4 строками в table1
!
Это очень простой запрос, возвращающий очень мало записей. Почему Postgres не может выполнить его должным образом?