Найти похожие строки с 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, но не с помощью ДЖИН индексы.