PostgreSQL: Як зробити "нечутливий до регістру" запит


338

Чи є спосіб записати нечутливі до регістру запити в PostgreSQL, наприклад, я хочу, щоб наступні 3 запити повертали той же результат.

SELECT id FROM groups where name='administrator'

SELECT id FROM groups where name='ADMINISTRATOR'

SELECT id FROM groups where name='Administrator'

якщо citext поставляється з установкою Postgres, спробуйте тип citext. Це нечутливий до регістру текст
Майкл Буен

2
Для новачків із цим питанням це посилання на офіційну документацію після пошти містить усі відповіді, наведені тут, а також кілька інших варіантів.
Парфянський розстріл

Сер переназначив прийняту відповідь на ту, яку зробив @Arun будь ласка. Це набагато менш складно і не тягнути купу клопотів після застосування.
зелібоба

Відповіді:


451

Використовуйте функцію LOWER для перетворення рядків у малі регістри перед порівнянням.

Спробуйте це:

SELECT id 
  FROM groups
 WHERE LOWER(name)=LOWER('Administrator')

92
Важливо зауважити, що використання LOWER (або будь-якої функції) у стовпцях предикатів - у цьому випадку "ім'я" - призведе до того, що будь-які індекси більше не будуть шукатими. Якщо це велика або часто запитувана таблиця, це може спричинити проблеми. Незалежне до регістру порівняння, citext або індекс на основі функцій покращать ефективність.
Йорданія

108
Або просто створіть такий індекс: CREATE INDEX idx_groups_name ON групи нижчі (назва);
Даніель

19
Вкажіть також, varchar_pattern_opsчи хочете ви, щоб індекс працював із LIKE 'xxx%'запитом, тобто CREATE INDEX ix_groups_name ON groups (lower(name) varchar_pattern_ops).
sayap

10
Використання оператора ILIKE (як показано в інших відповідях нижче) є більш простим підходом, хоча ця відповідь є найбільш голосовою.
Райан

5
Проходячи коментарі тут, багато пропозицій тут говорить ILIKE, він буде працювати, but with slow response. Щоб отримати швидкий доступ до таблиць на основі результатів обчислень, я пропоную кожному, хто просто перевірив це, повинен відповідати прийнятій відповіді. Детальніше дивіться тут і тут
Afolabi Olaoluwa Akinwumi

230

використовуючи ILIKEзамістьLIKE

SELECT id FROM groups WHERE name ILIKE 'Administrator'

1
Зверніть увагу, що ILIKEHibernate не підтримується при використанні у Spring Boot.
ANT

@AnT працює з org.hibernate.dialect.PostgreSQL94DialectSpring Boot 2.0.6.RELEASE. Але IntelliJ скаржиться на це.
Самінта Кавееш

134

Найпоширеніший підхід - це пошук у великому або великому регістрі пошукового рядка та даних. Але з цим є дві проблеми.

  1. Він працює англійською мовою, але не на всіх мовах. (Можливо, навіть не на більшості мов.) Не кожна маленька літера має відповідну велику літеру; не кожна велика літера має відповідну малу літеру.
  2. Використання таких функцій, як нижній () та верхній (), дасть вам послідовне сканування. Він не може використовувати індекси. У моїй тестовій системі використання нижче () займає приблизно в 2000 разів більше часу, ніж запит, який може використовувати індекс. (Дані тесту мають трохи більше 100 тис. Рядків.)

Існують щонайменше три менш часто використовувані рішення, які можуть бути ефективнішими.

  1. Використовуйте модуль citext , який здебільшого імітує поведінку типу даних, нечутливих до регістру. Завантаживши цей модуль, ви можете створити нечутливий до регістру індекс за допомогою CREATE INDEX ON groups (name::citext);. (Але див. Нижче.)
  2. Використовуйте порівняння з урахуванням регістру. Це встановлюється при ініціалізації бази даних. Використання нечутливого до регістру порівняння означає, що ви можете прийняти майже будь-який формат із коду клієнта, і ви все одно повернете корисні результати. (Це також означає, що ви не можете робити запити з урахуванням регістру. Дух.)
  3. Створіть функціональний індекс. Створіть малий індекс за допомогою CREATE INDEX ON groups (LOWER(name));. Зробивши це, ви можете скористатися індексом з такими запитами, як інакше SELECT id FROM groups WHERE LOWER(name) = LOWER('ADMINISTRATOR');, або SELECT id FROM groups WHERE LOWER(name) = 'administrator';вам потрібно пам’ятати про використання LOWER ().

Модуль citext не забезпечує справжній тип даних, нечутливий до регістру. Натомість вона поводиться так, ніби кожна рядок має нижній регістр. Тобто він поводиться так, ніби ви закликали lower()кожну рядок, як у номері 3 вище. Перевага полягає в тому, що програмістам не доводиться запам'ятовувати рядкові рядки. Але вам потрібно прочитати розділи "Поведінка порівняння рядків" та "Обмеження" в документах, перш ніж ви вирішите використовувати citext.


1
Про №1: це не повинно бути проблемою, оскільки це будуть дві різні рядки (подумайте про це як col = 'a'і робите col = 'b'). Про №2: Як ви вже говорили, ви можете створити індекс на виразі, тому це насправді не проблема. Але я погоджуюся з вами, що зміна порівняння - це, швидше за все, найкраще рішення.
Вінсент Савард

5
Хтось може сказати мені, які невідчутливі до регістру порівняння - це вбудовані порівняння PostgreSQL? Я вважаю це варіантом, але не можу знайти нічого в порівнянні з невідчутним до регістру для Postgres в мережі?
хорват

1
@AnupShah: Ні, я цього не кажу. Я не запускаю PostgreSQL в Windows. 9.4 документа говорять про це : "На всіх платформах доступні порівняння, названі за замовчуванням, C та POSIX. Додаткові порівняння можуть бути доступні залежно від підтримки операційної системи." Ви можете побачити, з якими посиланнями вважає PostgreSQL select * from pg_collation;.
Майк Шеррілл 'Згадка про котів'

1
@Matthieu: Це найкраще вступ (і обережність) до теми, про яку я знаю: Крайні випадки, які слід тримати в думці. Частина 1 - Текст .
Майк Шеррілл 'Відкликання котів'


95

Можна використовувати ILIKE. тобто

SELECT id FROM groups where name ILIKE 'administrator'

Його правильний і добре працює для мене, я використовую MAC OS X (Mountain Lion).
ADJ

5
Це буде працювати, але з повільною реакцією. Для отримання швидкого доступу до таблиць на основі результатів обчислень пропоную скористатися lowerфункцією. Інші подробиці
Афолабі Olaoluwa Akinwumi

1
@AfolabiOlaoluwaAkinwumi принципово це зводиться до ви шукаєте для результатів протистоять фільтрації відомих значень. В останньому випадку на рівні даних слід зберігати єдиний єдиний випадок, що дозволяє оператору рівності працювати. [Особиста рекомендація - це верхній регістр для значень коду]
Кріс Марісіч

53

Ви також можете прочитати ILIKEключове слово. Він може бути дуже корисним часом, хоча він не відповідає стандарту SQL. Дивіться тут для отримання додаткової інформації: http://www.postgresql.org/docs/9.2/static/functions-matching.html


9
На що слід звернути увагу - це зловмисне введення користувача. Якщо ви виконуєте такий запит email ILIKE 'user-input-email-here', не забудьте ввести користувацькі дані. В іншому випадку люди можуть вводити символи на зразок%, які відповідають будь-якому.
Метт Де Леон

2
@MattDeLeon Привіт. Добре сказано. Але я просто хочу запитати, якщо я використовую ILIKEі prepared statementsбуде це захистить мене від sql injection?
слевін

Не впевнений, я вважаю, що ви хочете відправити рядок втечі до підготовленого оператора.
Метт Де Леон

1
"Ключове слово ILIKE може бути використане замість LIKE, щоб зробити відповідність регістру невідповідним відповідно до активної мови. Це не в стандарті SQL, але є розширенням PostgreSQL." Працює як шарм у 9.3
Олексій Дерягін

1
ILIKE повільніше, ніж lower(column_name) like %expression%.
Патрик Імоса

28

Ви також можете використовувати регулярні вирази POSIX, наприклад

SELECT id FROM groups where name ~* 'administrator'

SELECT 'asd' ~* 'AsD' повертає t


1
У мене була така ж проблема, мені були потрібні нечутливі до регістру пошуки в моїй базі даних PostgreSQL. Я думав про перетворення рядка введення користувача у звичайний вираз. Тепер, використовуючи ~ * замість = або LIKE працював ідеально! Мені не потрібно було створювати нові індекси, стовпці чи що завгодно. Звичайно, пошук регулярних виразів відбувається повільніше, ніж порівняння прямого байта, але я не думаю, що вплив на продуктивність буде набагато більшим, ніж обробляти два набори даних (один нижній або верхній регістр лише для пошуку, а потім потрібно отримати відповідний оригінал дані з іншого набору). Крім того, це чистіше!
Кіберночі

1
Чудово, але як зробити, наприклад, з regexp_matches ()?
WKT

Згідно з документами postgres: Оператор ~~ еквівалентний LIKE, а ~~ * відповідає ILIKE. Є також оператори! ~~ і! ~~ *, які представляють НЕ ЛІКЕ та НЕ ІЛІКЕ відповідно. Усі ці оператори мають PostgreSQL.
sh4

Я зіткнувся з проблемою, коли дужки містяться в тексті, він не працює. як: "код (LC)"
Ошан Вісумперума

8

Використання ~*може значно покращити продуктивність завдяки функціональності INSTR.

SELECT id FROM groups WHERE name ~* 'adm'

повернути рядки з ім'ям, яке містить АБО, дорівнює "adm".


1
Гей, Робін, ласкаво просимо до ТА. Відповідь Джеймса Брауна вже пропонувала це рішення. Крім того, запропонована Вами відповідь жодним чином не використовує регулярний вираз.
Рафаель
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.