Ответ 1
Для этого используйте модуль unacent, который полностью отличается от того, на что вы ссылаетесь.
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');
ИндексIndex
Чтобы использовать индекс для этого типа запроса, создайте индекс для выражения. Однако, Postgres принимает только функции IMMUTABLE
для индексов. Если функция может вернуть другой результат для одного и того же ввода, индекс может молча прерваться.
unaccent()
только STABLE
не IMMUTABLE
К сожалению, unaccent()
является только STABLE
, а не IMMUTABLE
. Согласно этой теме о pgsql-ошибках, это связано с тремя причинами:
- Это зависит от поведения словаря.
- К этому словарю нет проводного соединения.
- Поэтому он также зависит от тока
search_path
, который может легко меняться.
Некоторые учебные пособия в Интернете предписывают просто изменить волатильность функции на IMMUTABLE
. Этот метод грубой силы может сломаться при определенных условиях.
Другие предлагают простую IMMUTABLE
функцию-обертку (как я делал это раньше).
В настоящее время ведутся споры о создании варианта с двумя параметрами IMMUTABLE
, который явно объявляет используемый словарь. Читайте здесь или здесь.
Другой альтернативой может быть этот модуль с функцией IMMUTABLE unaccent()
от Musicbrainz, предоставляемой на Github. Сам не проверял. Я думаю, что придумал лучшую идею:
Лучше всего сейчас
Этот подход более эффективен, чем другие решения, и более безопасен.
Создайте функцию-оболочку SQL IMMUTABLE
, выполнив двухпараметрическую форму со встроенной функцией, определяемой схемой, и словарем.
Поскольку вложение неизменяемой функции отключило бы встраивание функции, основывайте ее на копии 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
для обработки всех диакритические знаки известны Юникоду и правильно расширяют лигатуры (Томас Мунро, Леонард Бенедетти)
Жирный акцент мой. Теперь мы получаем:
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
также предоставляет полезные операторы для "сходства" (%
) и "расстояния" (<->
).
Индексы триграмм также поддерживают простые регулярные выражения с ~
и соавт. и регистр без учета регистра с ILIKE
: