Индекс GIN не работает с `SELECT 1`, но он работает, если я делаю `SELECT COUNT (*)` в PostgreSQL

#postgresql #indexing

#postgresql #индексирование

Вопрос:

У меня есть следующий запрос

 > explain analyze SELECT 1 AS one FROM "orders" WHERE "orders"."email" 
ILIKE '%email@gmail.com%' LIMIT 1 OFFSET 0

QUERY PLAN
Limit  (cost=0.00..470.44 rows=1 width=4) (actual time=2303.032..2303.033 rows=1 loops=1)
  Output: 1
  ->  Seq Scan on public.orders  (cost=0.00..108200.10 rows=230 width=4) (actual time=2303.031..2303.031 rows=1 loops=1)
        Output: 1
        Filter: ((orders.email)::text ~~* '%email@gmail.com%'::text)
        Rows Removed by Filter: 2309367
Planning Time: 0.195 ms
Execution Time: 2303.047 ms  
  

Если я выполняю тот же запрос, но вместо использования SELECT 1 я использую SELECT COUNT(*) индекс gin (gin_trgm_ops), начинает работать

 > explain analyze SELECT COUNT(*) FROM "orders" WHERE "orders"."email" 
ILIKE '%email@gmail.com%' LIMIT 1 OFFSET 0

QUERY PLAN

Limit  (cost=1263.98..1263.99 rows=1 width=8) (actual time=18.074..18.075 rows=1 loops=1)
  ->  Aggregate  (cost=1263.98..1263.99 rows=1 width=8) (actual time=18.073..18.073 rows=1 loops=1)
        ->  Bitmap Heap Scan on orders  (cost=377.78..1263.40 rows=230 width=0) (actual time=18.062..18.067 rows=3 loops=1)
              Recheck Cond: ((email)::text ~~* '%email@gmail.com%'::text)
              Heap Blocks: exact=2
              ->  Bitmap Index Scan on index_orders_on_email_gin  (cost=0.00..377.72 rows=230 width=0) (actual time=18.043..18.044 rows=3 loops=1)
                    Index Cond: ((email)::text ~~* '%email@gmail.com%'::text)
Planning Time: 0.575 ms
Execution Time: 18.120 ms
  

Есть идеи, почему?

Спасибо

Ответ №1:

С помощью SELECT 1 … ОГРАНИЧЕНИЕ 1, он может остановиться раньше, как только найдет одну соответствующую строку. Поскольку PostgreSQL неверно оценивает количество соответствующих строк, он неверно оценивает, насколько полезной будет эта ранняя остановка.

ОГРАНИЧЕНИЕ ничего не делает при использовании с COUNT (*), но без GROUP BY, поскольку в любом случае возвращается только одна строка. Преждевременной остановки не может быть сделано, так как для их подсчета необходимо найти каждую соответствующую строку.

Суть вопроса не в том, чтобы ВЫБРАТЬ 1 по сравнению с SELECT COUNT (*), это ОГРАНИЧЕНИЕ, которое делает что-то по сравнению с тем, что не делает.