Найти похожие строки с PostgreSQL быстро

мне нужно создать рейтинг подобных строк в таблице.

у меня есть следующие таблицы

create table names (
name character varying(255)
);

В настоящее время я использую pg_trgm модуль, который предлагает similarity функция, но у меня есть проблема с эффективностью. Я создал индекс, подобный руководство Postgres предлагает:

CREATE INDEX trgm_idx ON names USING gist (name gist_trgm_ops);

и я выполняю следующий запрос:

select (similarity(n1.name, n2.name)) as sim, n1.name, n2.name
from names n1, names n2
where n1.name != n2.name and similarity(n1.name, n2.name) > .8
order by sim desc;

запрос работает, но очень медленно, когда у вас есть сотни имен. Более того, возможно, я забыл немного SQL, но я не понимаю, почему я не могу использовать условие and sim > .8 без получения ошибки" столбец sim не существует".

Я хотел бы любой намек, чтобы сделать запрос быстрее.

1 ответов


обновление: на Postgres 9.6 (бета-версия на момент написания) функции set_limit() и show_limit() заменяются параметром конфигурации pg_trgm.similarity_threshold (наряду с несколькими другими улучшениями модуля pg_trgm). Функции устарели, но все еще работают.

кроме того, производительность индексов GIN и GiST была улучшена несколькими способами с Postgres 9.1.


использовать set_limit() и % оператор. Оба предоставляются pg_trgm.

как у вас это есть, сходство между каждым элементом и каждым другим элементом таблицы должно быть вычислено (почти перекрестное соединение). Если ваша таблица имеет 1000 строк, это 1,000,000 (!) расчетное сходство,до те можно проверить с условием и сортировкой. Попробуйте:

SELECT set_limit(0.8);

SELECT similarity(n1.name, n2.name) AS sim, n1.name, n2.name
FROM   names n1
JOIN   names n2 ON n1.name <> n2.name
               AND n1.name % n2.name
ORDER  BY sim DESC;

должно быть быстрее на порядки, но все равно будет медленным.

вы можете ограничить количество возможных пар, введя больше предварительных условий (например, соответствие первой буквы) до перекрестное присоединение (и поддержка с соответствующим функциональным индексом). Производительность перекрестное соединение ухудшается квадратично С ростом числа записей - O (N2).


Что касается вашего дополнительного вопроса:

WHERE ... sim > 0.8

не работает потому что ты не может обратитесь к выходным столбцам в WHERE или HAVING положения. Это в соответствии с (немного запутанным, предоставленным) стандартом SQL , который обрабатывается довольно свободно некоторыми другими СУБД.

С другой стороны:

ORDER BY sim DESC

работает потому что выходные столбцы can использоваться в GROUP BY и ORDER BY. Подробности:

тест

я провел быстрый тест на моем старом тестовом сервере, чтобы проверить мои утверждения.
В PostgreSQL 9.1.4. Времена взяты с EXPLAIN ANALYZE (лучший из пять).

CREATE TEMP table t AS 
SELECT some_col AS name FROM some_table LIMIT 1000;  -- real life test strings

первый раунд тестов с индексом Джина:

CREATE INDEX t_gin ON t USING gin(name gin_trgm_ops);  -- round1: with GIN index

второй раунд испытаний с Индекс GIST:

DROP INDEX t_gin;
CREATE INDEX t_gist ON t USING gist(name gist_trgm_ops);

новый запрос:

-- SELECT show_limit();
SELECT set_limit(0.8);   -- fewer hits and faster with higher limit

SELECT similarity(n1.name, n2.name) AS sim, n1.name, n2.name
FROM   t n1
JOIN   t n2 ON n1.name <> n2.name
           AND n1.name % n2.name
ORDER  BY sim DESC;

используемый индекс Джина, 64 попадания: общее время выполнения: 484.022 ms
Используемый индекс GIST, 64 хита: общее время выполнения:248.772 ms

старый запрос:

SELECT (similarity(n1.name, n2.name)) as sim, n1.name, n2.name
FROM   t n1, t n2
WHERE  n1.name != n2.name
AND    similarity(n1.name, n2.name) > 0.8
ORDER  BY sim DESC;

индекс Джина не используется, 64 попадания: общее время выполнения: 6345.833 ms
Индекс GIST не используется, 64 хита: общее время выполнения: 6335.975 ms

в противном случае идентичные результаты. Совет хорош. А это для всего 1000 строк.

джин или Гист?

Джин часто обеспечивает превосходную скорость чтения:

но не в этом конкретном случае:

это может быть реализовано довольно эффективно с помощью индексов GiST, но не с помощью ДЖИН индексы.