У меня есть таблица со следующим определением:
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.