Используйте для этого модуль unaccent , который полностью отличается от того, на что вы ссылаетесь.
unaccent - это словарь текстового поиска, удаляющий акценты (диакритические знаки) из лексем.
Установите один раз для каждой базы данных с помощью:
CREATE EXTENSION unaccent;
Если вы получите сообщение об ошибке:
ERROR: could not open extension control file
"/usr/share/postgresql/<version>/extension/unaccent.control": No such file or directory
Установите пакет contrib на свой сервер базы данных, как указано в соответствующем ответе:
Среди прочего, он предоставляет функцию, которую unaccent()
вы можете использовать в своем примере (там, где это LIKE
кажется ненужным).
SELECT *
FROM users
WHERE unaccent(name) = unaccent('João');
Индекс
Чтобы использовать индекс для такого типа запроса, создайте индекс для выражения . Однако Postgres принимает IMMUTABLE
функции только для индексов. Если функция может вернуть другой результат для одного и того же ввода, индекс может молча прерваться.
unaccent()
только STABLE
неIMMUTABLE
К сожалению, unaccent()
только STABLE
нет IMMUTABLE
. Согласно этой теме на pgsql-bugs , это происходит по трем причинам:
- Это зависит от поведения словаря.
- К этому словарю нет проводного подключения.
- Следовательно, это также зависит от тока
search_path
, который может легко измениться.
Некоторые обучающие программы в Интернете инструктируют просто изменить волатильность функции на IMMUTABLE
. Этот метод грубой силы может сломаться при определенных условиях.
Другие предлагают простую IMMUTABLE
функцию-оболочку (как я сам делал в прошлом).
Продолжаются дискуссии о том, следует ли делать вариант с двумя параметрами, IMMUTABLE
который явно объявляет используемый словарь. Читайте здесь или здесь .
Другая альтернатива была бы этот модуль с непреложной unaccent()
функцией по MusicBrainz , представленной на Github. Сам не тестировал. Думаю, я придумал лучшую идею :
Лучшее на данный момент
Этот подход более эффективен, чем другие решения, и безопаснее .
Создайте функцию- IMMUTABLE
оболочку SQL, выполняющую двухпараметрическую форму с зашитой функцией и словарем, квалифицированной схемой.
Поскольку вложение неизменяемой функции отключит встраивание функции, основывайте его на копии объявленной C-функции (поддельной) IMMUTABLE
. Его единственное назначение - использовать в оболочке функции SQL. Не предназначен для использования отдельно.
Изощренность необходима, поскольку нет возможности жестко привязать словарь к объявлению функции C. (Потребуется взломать сам код C.) Функция-оболочка SQL делает это и позволяет как встраивание функций, так и индексы выражений.
CREATE OR REPLACE FUNCTION public.immutable_unaccent(regdictionary, text)
RETURNS text LANGUAGE c IMMUTABLE PARALLEL SAFE STRICT AS
'$libdir/unaccent', 'unaccent_dict';
CREATE OR REPLACE FUNCTION public.f_unaccent(text)
RETURNS text LANGUAGE sql IMMUTABLE PARALLEL SAFE STRICT AS
$func$
SELECT public.immutable_unaccent(regdictionary 'public.unaccent', $1)
$func$;
Откажитесь PARALLEL SAFE
от обеих функций для Postgres 9.5 или старше.
public
Схема, в которую вы установили расширение ( public
по умолчанию).
Явное объявление типа ( regdictionary
) защищает от гипотетических атак злонамеренных пользователей с перегруженными вариантами функции.
Ранее я выступал за функцию-оболочку, основанную на STABLE
функции, unaccent()
поставляемой с модулем unaccent. Эта отключенная функция встраивается . Эта версия выполняется в десять раз быстрее, чем простая функция-оболочка, которую я использовал здесь ранее.
И это было уже в два раза быстрее, чем в первой версии, добавленной SET search_path = public, pg_temp
к функции - пока я не обнаружил, что словарь также может быть квалифицирован по схеме. Тем не менее (Postgres 12) не слишком очевиден из документации.
Если у вас нет необходимых привилегий для создания функций C, вы вернетесь ко второй лучшей реализации: IMMUTABLE
обертка функции вокруг STABLE
unaccent()
функции, предоставляемой модулем:
CREATE OR REPLACE FUNCTION public.f_unaccent(text)
RETURNS text AS
$func$
SELECT public.unaccent('public.unaccent', $1) -- schema-qualify function and dictionary
$func$ LANGUAGE sql IMMUTABLE PARALLEL SAFE STRICT;
Наконец, индекс выражения для быстрого выполнения запросов :
CREATE INDEX users_unaccent_name_idx ON users(public.f_unaccent(name));
Не забудьте воссоздать индексы, включающие эту функцию, после любого изменения функции или словаря, например, при обновлении основного выпуска на месте, при котором индексы воссоздавать не будут. Все последние основные выпуски содержали обновления unaccent
модуля.
Адаптируйте запросы к индексу (чтобы планировщик запросов использовал его):
SELECT * FROM users
WHERE f_unaccent(name) = f_unaccent('João');
Вам не нужна функция в правильном выражении. Там вы также можете указать строки без ударения, например, 'Joao'
напрямую.
Более быстрая функция не преобразуется в гораздо более быстрые запросы с использованием индекса выражения . Это работает с предварительно вычисленными значениями и уже работает очень быстро. Но обслуживание индекса и запросы, не использующие преимущества индекса.
Безопасность клиентских программ была усилена с помощью Postgres 10.3 / 9.6.8 и т. Д. Вам необходимо указать функцию и имя словаря с указанием схемы, как показано при использовании в любых индексах. Видеть:
Лигатуры
В Postgres 9.5 или более ранних лигатуры, такие как 'Œ' или 'ß', должны быть расширены вручную (если вам это нужно), поскольку unaccent()
всегда заменяет одну букву:
SELECT unaccent('Œ Æ œ æ ß');
unaccent
----------
E A e a S
Вам понравится это обновление до Unaccent в Postgres 9.6 :
contrib/unaccent
Стандартный unaccent.rules
файл Extend для обработки всех диакритических знаков, известных в Unicode, и правильного расширения лигатур (Томас Манро, Леонард Бенедетти)
Смелый акцент мой. Теперь получаем:
SELECT unaccent('Œ Æ œ æ ß');
unaccent
----------
OE AE oe ae ss
Сопоставление с образцом
Для произвольных шаблонов LIKE
или ILIKE
с ними объедините это с модулем pg_trgm
в PostgreSQL 9.1 или новее. Создайте триграмму GIN (обычно предпочтительнее) или индекс выражения GIST. Пример для GIN:
CREATE INDEX users_unaccent_name_trgm_idx ON users
USING gin (f_unaccent(name) gin_trgm_ops);
Может использоваться для таких запросов:
SELECT * FROM users
WHERE f_unaccent(name) LIKE ('%' || f_unaccent('João') || '%');
Индексы GIN и GIST обходятся дороже, чем обычные btree:
Есть более простые решения только для шаблонов с левым якорем. Подробнее о сопоставлении с образцом и производительности:
pg_trgm
также предоставляет полезные операторы для «подобия» ( %
) и «расстояния» ( <->
). .
Индексы триграммы также поддерживают простые регулярные выражения с помощью ~
et al. и чувствителен к регистру сопоставление с образцом с ILIKE
: