Звичайно база знаходить у індексі посилання на рядок, а потім іде в саму таблицю по решту колонок. Якщо всі колонки, потрібні запиту, вже є в індексі, звертатися до таблиці не треба - це index-only scan, а індекс називають покривним.
-- Запит
SELECT status, total FROM orders WHERE user_id = 5;
-- Покривний індекс: user_id для пошуку, status і total - щоб не йти в таблицю
CREATE INDEX orders_user_cover_idx ON orders (user_id) INCLUDE (status, total);
INCLUDE (PostgreSQL 11+) додає колонки лише в листки індексу: за ними не шукають, зате їх можна прочитати. У MySQL INCLUDE немає - колонки просто додають у кінець складеного індексу.
Нюанси:
- PostgreSQL через MVCC мусить перевірити, чи рядок видимий транзакції. Index-only scan справді не читає таблицю лише для сторінок, позначених у visibility map як «усі рядки видимі». Її оновлює
VACUUM, тож на таблиці з частими змінами без вакууму виграш менший. УEXPLAIN ANALYZEце видно якHeap Fetches. - InnoDB усі вторинні індекси вже містять первинний ключ, тож
SELECT id ... WHERE email = ?за індексом наemail- покривний автоматично. - Широкий покривний індекс - це копія частини таблиці: більше місця, повільніший запис. Його роблять під конкретний частий запит, а не «про всяк випадок».
Докладніше в документації: Index-only scan і покривні індекси