Эффективный запрос для поиска строк, соответствующих правилу на основе нескольких строк - PullRequest
0 голосов
/ 05 апреля 2020

У меня есть таблица PostgreSQL чешских слов (> 1M строк) со столбцом с именем "word" [текст], и я хочу найти все слова с одинаковым склонением (см. Чешское склонение ) основанный на окончании слова.

Например, я хочу найти все слова, которые заканчиваются на "e" (например, kuře), но для которых также существуют словоформы, которые заканчиваются на "ete" (например, kuřete), а также " etem "(например, kuřetem), а также" eti "(например, kuřeti). Для каждого слова существует ок. 14 словоформ.

Как эффективный способ (SQL запрос) найти все слова, которые соответствуют правилу?

Ответы [ 2 ]

2 голосов
/ 05 апреля 2020

Это случай реляционного деления .

Предполагая таблицу из UNIQUE слов вроде:

CREATE TABLE words (word text PRIMARY KEY);

Это должно быть одним из самых быстрых возможных решений:

SELECT w0.stem
FROM  (
   SELECT left(word, -4) AS stem  -- -4 = length('etem')
   FROM   words
   WHERE  word LIKE '%etem'  -- pick the most selective ending to get started
   ) w0
JOIN   words w1 ON w1.word = stem || 'eti'
JOIN   words w2 ON w2.word = stem || 'ete'
JOIN   words w3 ON w3.word = stem || 'e';

Находит все основы слова, которые появляются с все данные окончания. Больше слов, начинающихся с одного и того же стебля и разных окончаний, не приводят к дисквалификации!

Если вам нужно проверить множество концовок (14?), Может быть утомительно все это изложить. Более короткий код, как правило, медленнее:

SELECT w0.stem
FROM  (
   SELECT left(word, -4) AS stem
   FROM   words
   WHERE  word LIKE '%etem'  -- pick the most selective ending to get started
   ) w0
CROSS  JOIN unnest ('{eti,ete,e}'::text[]) x(dec)  -- all other in an array
JOIN   words w1 ON w1.word = w0.stem || x.dec
GROUP  BY w0.stem
HAVING count(*) = 3;  -- = cardinality('{eti,ete,e}'::text[])

дБ <> скрипка здесь

Связанные:

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

0 голосов
/ 05 апреля 2020

Хммм. , , если я правильно понимаю, то это не , что эффективная операция. Но я думаю, что агрегирование может быть самым быстрым подходом, если вы не погрузитесь в тайные стратегии индексации (которые даже тогда могут не работать):

select left(word, length(word) - 1) || 'e'
from words w
where word ~ '(e|ete|etem)$'
group by left(word, length(word) - 1);
...