У базі даних Postgres 9.1 у мене є таблиця table1
з ~ 1.5M рядками та стовпцем label
(спрощені назви заради цього питання).
Існує функціональний триграм-індекс на lower(unaccent(label))
( unaccent()
був незмінний, щоб дозволити його використання в індексі).
Наступний запит досить швидкий:
SELECT count(*) FROM table1
WHERE (lower(unaccent(label)) like lower(unaccent('%someword%')));
count
-------
1
(1 row)
Time: 394,295 ms
Але наступний запит повільніше:
SELECT count(*) FROM table1
WHERE (lower(unaccent(label)) like lower(unaccent('%someword and some more%')));
count
-------
1
(1 row)
Time: 1405,749 ms
А додавання більше слів ще повільніше, хоча пошук жорсткіший.
Я спробував простий трюк, щоб запустити підзапит на перше слово, а потім на запит з повною рядком пошуку, але (на жаль) планувальник запитів побачив мої махінації:
EXPLAIN ANALYZE
SELECT * FROM (
SELECT id, title, label from table1
WHERE lower(unaccent(label)) like lower(unaccent('%someword%'))
) t1
WHERE lower(unaccent(label)) like lower(unaccent('%someword and some more%'));
Сканування бітної карти на таблицю1 (вартість = 16216.01..16220.04 рядків = 1 ширина = 212) (фактичний час = 1824.017..1824.019 рядків = 1 петля = 1) Перевірити умову: ((нижня (нецентральна ((мітка): :: текст)) ~~ '% someword%' :: текст) І (нижня (неприйнятна ((мітка): :: текст)) ~~ '% деяке слово та ще деякі % ':: текст)) -> Сканування індексу растрових зображень на table1_label_hun_gin_trgm (вартість = 0,00..16216.01 рядків = 1 ширина = 0) (фактичний час = 1823.900..1823.900 рядків = 1 петля = 1) Індекс Cond: ((нижній (невідповідний ((мітка): :: текст)) ~~ '% someword%' :: текст) AND (нижній (невідповідний ((мітка) :: текст)) ~ ~ '% someword та ще деякі % ':: текст)) Загальна тривалість виконання: 1824,064 мс
Моя остання проблема полягає в тому, що пошуковий рядок надходить із веб-інтерфейсу, який може надсилати досить довгі рядки і, таким чином, бути досить повільним, а також може становити вектор DOS.
Отже, мої запитання:
- Як пришвидшити запит?
- Чи є спосіб розбити його на підзапити, щоб це було швидше?
- Можливо, пізніша версія Postgres краща? (Я спробував 9.4, і здається, не швидше: все-таки такий же ефект. Можливо, пізніша версія?)
- Можливо, потрібна інша стратегія індексації?
unaccent
непорушну. Я додав це до питання.
unaccent
модуля. Одна з причин, чому я пропоную замість цього функціональну обгортку.
unaccent()
також надається додатковий модуль, і Postgres не підтримує індекси функції за замовчуванням, оскільки це не такIMMUTABLE
. Ви, мабуть, щось змінили, і у своєму запитанні слід згадати, що саме ви зробили. Моя постійна порада: stackoverflow.com/a/11007216/939860 . Крім того, триграмні індекси підтримують невідчутну до регістру відповідність. Ви можете спростити:WHERE f_unaccent(label) ILIKE f_unaccent('%someword%')
- з відповідним індексом. Детальніше: stackoverflow.com/a/28636000/939860 .