Почему индекс триграмм GIN не используется с предложением ILIKE ANY? - PullRequest
1 голос
/ 04 марта 2020

У меня есть таблица со следующим определением:

CREATE TABLE clients
        (
            "id" SERIAL PRIMARY KEY,
            "email" TEXT NOT NULL,
            "first_name" TEXT NOT NULL,
            "last_name" TEXT NOT NULL,
            "telephone" TEXT,
            "city" TEXT NOT NULL,
            "street" TEXT NOT NULL,
            "house" TEXT NOT NULL,
            "apartment" TEXT,
            "created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
            "updated_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
        );

Я хочу выполнить поиск путем частичного совпадения любых слов в запросе с любым из большинства полей в таблице. У меня есть индекс, который я намерен использовать для ускорения поиска:

create index "clients_text_search_idx" on clients
    using gin ((
        first_name || ' ' ||
        last_name || ' ' ||
        coalesce(telephone, '') || ' ' ||
        email || ' ' ||
        city || ' ' ||
        street || ' ' ||
        house || ' ' ||
        coalesce(apartment, '')
        ) gin_trgm_ops);

Я заполняю свою таблицу реалистичными c поддельными данными, создавая более 100 000 строк. Затем я хочу, чтобы мой индекс использовался со следующим запросом, используя то же выражение, которое я использовал при создании моего индекса:

explain analyse select * from clients where (
        first_name || ' ' ||
        last_name || ' ' ||
        coalesce(telephone, '') || ' ' ||
        email || ' ' ||
        city || ' ' ||
        street || ' ' ||
        house || ' ' ||
        coalesce(apartment, '')
        ) ilike any (
    select '%' || word || '%'
    from regexp_split_to_table('+123 georg', E'\\s+') AS word); -- same as: ilike any (values ('%+123%'), ('%georg%'))

Однако я вижу простое последовательное сканирование (даже если я использую set enable_seqscan = false):

Gather  (cost=1000.00..4027885.59 rows=550 width=135) (actual time=2.083..4542.739 rows=166 loops=1)
  Workers Planned: 1
  Workers Launched: 1
  ->  Nested Loop Semi Join  (cost=0.00..4026830.59 rows=324 width=135) (actual time=14.319..4503.745 rows=83 loops=2)
"        Join Filter: (((((((((((((((clients.first_name || ' '::text) || clients.last_name) || ' '::text) || COALESCE(clients.telephone, ''::text)) || ' '::text) || clients.email) || ' '::text) || clients.city) || ' '::text) || clients.street) || ' '::text) || clients.house) || ' '::text) || COALESCE(clients.apartment, ''::text)) ~~* (('%'::text || word.word) || '%'::text))"
        Rows Removed by Join Filter: 109928
        ->  Parallel Seq Scan on clients  (cost=0.00..2540.12 rows=64712 width=135) (actual time=0.032..533.627 rows=55006 loops=2)
        ->  Function Scan on regexp_split_to_table word  (cost=0.00..10.00 rows=1000 width=32) (actual time=0.009..0.028 rows=2 loops=110011)
Planning Time: 3.167 ms
Execution Time: 8.750 ms

Но если я заменим целое предложение ilike any ... предыдущего запроса чем-то простым, например ilike '%george%', индекс будет использован, и запрос будет выполнен очень быстро. Так почему же мой индекс не используется с предложением ilike any?

Я использую PostgreSQL 11.2 в MacOS Mojave.

Ответы [ 2 ]

2 голосов
/ 04 марта 2020

Я должен признать, что не понимаю, почему это происходит, но я смог заставить его работать, сделав правую часть массива ANY вместо подзапроса, используя ARRAY(<subselect>) form .

testdb=# explain analyse select * from clients where (
        first_name || ' ' ||
        last_name || ' ' ||
        coalesce(telephone, '') || ' ' ||
        email || ' ' ||
        city || ' ' ||
        street || ' ' ||
        house || ' ' ||
        coalesce(apartment, '')
        ) ilike any (array(
    select ('%' || word || '%')::text
    from regexp_split_to_table('foo georg', E'\\s+') AS word));
                                                                                                                                       QUERY PLAN                                                                                                                                        
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Bitmap Heap Scan on clients  (cost=126.79..238.91 rows=489 width=62) (actual time=2.108..18.721 rows=10000 loops=1)
   Recheck Cond: (((((((((((((((first_name || ' '::text) || last_name) || ' '::text) || COALESCE(telephone, ''::text)) || ' '::text) || email) || ' '::text) || city) || ' '::text) || street) || ' '::text) || house) || ' '::text) || COALESCE(apartment, ''::text)) ~~* ANY ($0))
   Heap Blocks: exact=84
   InitPlan 1 (returns $0)
     ->  Function Scan on regexp_split_to_table word  (cost=0.00..15.00 rows=1000 width=32) (actual time=0.043..0.045 rows=2 loops=1)
   ->  Bitmap Index Scan on clients_text_search_idx  (cost=0.00..111.67 rows=489 width=0) (actual time=2.060..2.060 rows=10000 loops=1)
         Index Cond: (((((((((((((((first_name || ' '::text) || last_name) || ' '::text) || COALESCE(telephone, ''::text)) || ' '::text) || email) || ' '::text) || city) || ' '::text) || street) || ' '::text) || house) || ' '::text) || COALESCE(apartment, ''::text)) ~~* ANY ($0))
 Planning time: 0.297 ms
 Execution time: 19.254 ms
(9 rows)

Это с enable_seqscan = off на стр. 10.12 (все, что у меня есть сейчас).

0 голосов
/ 04 марта 2020

Похоже, что ваш запрос выполняется в параллельном режиме, но только с одним работником, который не имеет смысла для меня: он не объясняет, почему индекс не используется, но выглядит странно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...