PostgreSQL: Jak wykonać zapytanie „bez rozróżniania wielkości liter”

338

Czy jest jakiś sposób na pisanie zapytań bez rozróżniania wielkości liter w PostgreSQL, np. Chcę, aby kolejne 3 zapytania zwróciły ten sam wynik.

SELECT id FROM groups where name='administrator'

SELECT id FROM groups where name='ADMINISTRATOR'

SELECT id FROM groups where name='Administrator'
Jame
źródło
jeśli citext jest dostarczany z instalacją Postgres, spróbuj wpisać citext. Jest to tekst bez rozróżniania wielkości liter
Michael Buen,
2
Dla nowych użytkowników tego linku ten link do oficjalnej dokumentacji postgres zawiera wszystkie podane tutaj odpowiedzi, a także kilka innych opcji.
Parthian Shot
Proszę o ponowne przypisanie zaakceptowanej odpowiedzi na odpowiedź udzieloną przez @Arun. Jest to o wiele mniej skomplikowane i nie ciągnij za kłopoty po nałożeniu.
zeliboba

Odpowiedzi:

451

Użyj DOLNEGO funkcji aby przekonwertować ciągi znaków na małe litery przed porównaniem.

Spróbuj tego:

SELECT id 
  FROM groups
 WHERE LOWER(name)=LOWER('Administrator')
Chandu
źródło
92
Należy zauważyć, że użycie NISKIEJ (lub dowolnej funkcji) w kolumnach predykatów - w tym przypadku „nazwa” - spowoduje, że żadne indeksy nie będą już widoczne. Jeśli jest to duży lub często pytany stół, może to powodować problemy. Sortowanie bez rozróżniania wielkości liter, cytowanie lub indeks oparty na funkcjach poprawi wydajność.
Jordan,
108
Lub po prostu utwórz taki indeks: CREATE INDEX idx_groups_name ON grupy lower (name);
Daniel,
19
Określ także, varchar_pattern_opsczy indeks ma współpracować z LIKE 'xxx%'zapytaniem, tj CREATE INDEX ix_groups_name ON groups (lower(name) varchar_pattern_ops).
sayap,
10
Korzystanie z operatora ILIKE (jak pokazano w innych odpowiedziach poniżej) jest prostszym podejściem, mimo że jest to najczęściej głosowana odpowiedź.
Ryan,
5
Przeżywa komentarzach tutaj wiele sugestii tutaj sugeruje ILIKE, będzie ona działać, but with slow response. Aby uzyskać szybki dostęp do tabel w oparciu o wyniki obliczeń, sugeruję, aby każdy, kto to sprawdził, powinien przyjąć zaakceptowaną odpowiedź. Zobacz więcej szczegółów tutaj i tutaj
Afolabi Olaoluwa Akinwumi
230

za pomocą ILIKEzamiastLIKE

SELECT id FROM groups WHERE name ILIKE 'Administrator'
Mohammad Reza Norouzi
źródło
1
Pamiętaj, że ILIKEnie jest obsługiwany przez Hibernację, gdy jest używany w Spring Boot.
AnT
@AtT działa z org.hibernate.dialect.PostgreSQL94Dialecti Spring Boot 2.0.6. ZWOLNIJ. Ale IntelliJ narzeka na to.
Samintha Kaveesh
134

Najczęstszym podejściem jest albo małe lub wielkie litery szukanego ciągu i danych. Ale są z tym dwa problemy.

  1. Działa w języku angielskim, ale nie we wszystkich językach. (Może nawet nie w większości języków.) Nie każda mała litera ma odpowiednią wielką literę; nie każda wielka litera ma odpowiednią małą literę.
  2. Korzystanie z funkcji, takich jak lower () i upper (), zapewni sekwencyjne skanowanie. Nie może używać indeksów. W moim systemie testowym użycie lower () zajmuje około 2000 razy dłużej niż zapytanie, które może korzystać z indeksu. (Dane testowe mają nieco ponad 100 tys. Wierszy).

Istnieją co najmniej trzy rzadziej stosowane rozwiązania, które mogą być bardziej skuteczne.

  1. Użyj modułu citext , który w większości naśladuje zachowanie typu danych bez rozróżniania wielkości liter. Po załadowaniu tego modułu możesz utworzyć indeks bez rozróżniania wielkości liter CREATE INDEX ON groups (name::citext);. (Ale patrz poniżej.)
  2. Użyj sortowania bez rozróżniania wielkości liter. Jest to ustawiane podczas inicjowania bazy danych. Użycie sortowania bez rozróżniania wielkości liter oznacza, że ​​możesz zaakceptować dowolny format z kodu klienta, a nadal zwrócisz przydatne wyniki. (Oznacza to również, że nie można wykonywać rozróżniania wielkości liter. Duh.)
  3. Utwórz indeks funkcjonalny. Utwórz indeks małych liter za pomocą CREATE INDEX ON groups (LOWER(name));. Po zrobieniu tego możesz skorzystać z indeksu z zapytaniami typu SELECT id FROM groups WHERE LOWER(name) = LOWER('ADMINISTRATOR');, lub SELECT id FROM groups WHERE LOWER(name) = 'administrator';musisz pamiętać, aby użyć LOWER ().

Moduł citext nie zapewnia prawdziwego typu danych bez rozróżniania wielkości liter. Zamiast tego zachowuje się tak, jakby każdy ciąg był pisany małymi literami. Oznacza to, że zachowuje się tak, jakbyś wywołał lower()każdy ciąg, jak w punkcie 3 powyżej. Zaletą jest to, że programiści nie muszą pamiętać o małych ciągach znaków. Ale zanim zdecydujesz się użyć citext, musisz przeczytać sekcje „Zachowanie porównywania ciągów” i „Ograniczenia” w dokumentacji.

Mike Sherrill „Cat Recall”
źródło
1
O nr 1: Nie powinno to stanowić problemu, ponieważ byłyby to dwa różne ciągi (pomyśl o tym jak o robieniu col = 'a'i col = 'b'). O nr 2: Jak powiedziałeś, możesz utworzyć indeks dla wyrażenia, więc tak naprawdę to nie jest problem. Ale zgadzam się z tobą, że zmiana zestawienia jest najprawdopodobniej najlepszym rozwiązaniem.
Vincent Savard,
5
Czy ktoś może mi powiedzieć, jakie sortowania bez rozróżniania wielkości liter są wbudowanymi sortowaniami PostgreSQL? Widzę to jako opcję, ale nie mogę znaleźć niczego na temat sortowania Postgres w sieci bez rozróżniania wielkości liter?
khorvat
1
@AnupShah: Nie, nie mówię tego. Nie używam PostgreSQL w systemie Windows. Dokumenty 9.4 mówią : „Na wszystkich platformach dostępne są sortowania o nazwach default, C i POSIX. W zależności od obsługi systemu operacyjnego mogą być dostępne dodatkowe sortowania”. Możesz zobaczyć, z którymi zestawieniami myśli PostgreSQL select * from pg_collation;.
Mike Sherrill „Cat Recall”
1
@ Matthieu: To najlepsze wprowadzenie (i ostrożność) do tematu, o którym wiem: Edge Cases do zapamiętania. Część 1 - Tekst .
Mike Sherrill „Cat Recall”
1
@Matthieu: Często zadawane pytania dotyczące Unicode są również zabawne. Oto dlaczego nie ma wyjątkowej wielkiej litery. . .
Mike Sherrill „Cat Recall”
95

Możesz użyć ILIKE. to znaczy

SELECT id FROM groups where name ILIKE 'administrator'
ADJ
źródło
Jest poprawny i działa dobrze dla mnie, używam MAC OS X (Mountain Lion).
ADJ
5
To zadziała, ale z powolną reakcją. Aby uzyskać szybki dostęp do tabel w oparciu o wyniki obliczeń, sugeruję użycie lowerfunkcji. Zobacz więcej szczegółów
Afolabi Olaoluwa Akinwumi,
1
@AfolabiOlaoluwaAkinwumi zasadniczo sprowadza się to do tego, czy szukasz wyników, a nie filtrowania znanych wartości. W tym drugim przypadku należy zachować jeden jednolity przypadek na poziomie danych, umożliwiając operatorowi równości działanie. [Osobiste zalecenie to górna część pascal dla wartości kodu typu]
Chris Marisic
53

Możesz także przeczytać ILIKEsłowo kluczowe. Czasami może być dość przydatny, choć nie jest zgodny ze standardem SQL. Zobacz tutaj, aby uzyskać więcej informacji: http://www.postgresql.org/docs/9.2/static/functions-matching.html

Priidu Neemre
źródło
9
Coś, na co trzeba uważać, to złośliwy wkład użytkowników. Jeśli uruchomisz zapytanie podobne email ILIKE 'user-input-email-here', pamiętaj, aby uciec od danych wejściowych użytkownika. W przeciwnym razie ludzie mogą wprowadzać znaki takie jak%, które pasują do dowolnego elementu.
Matt De Leon
2
@MattDeLeon Cześć. Dobrze powiedziane. Ale chcę cię tylko zapytać, czy używam ILIKEi prepared statementsczy to mnie ochroni sql injection?
slevin
Nie jestem pewien, przypuszczam, że chcesz wysłać ciąg znaków ucieczki do przygotowanej instrukcji.
Matt De Leon,
1
„Zamiast LIKE można użyć słowa kluczowego ILIKE, aby rozróżnić wielkość liter w zależności od aktywnych ustawień regionalnych. Nie jest to standard SQL, ale rozszerzenie PostgreSQL.” Działa jak urok w 9.3
Aleksey Deryagin
1
ILIKE jest wolniejszy niż lower(column_name) like %expression%.
Patryk Imosa
28

Możesz także użyć wyrażeń regularnych POSIX, takich jak

SELECT id FROM groups where name ~* 'administrator'

SELECT 'asd' ~* 'AsD' zwroty t

James Brown
źródło
1
Miałem ten sam problem, potrzebowałem rozróżniania wielkości liter w mojej bazie danych PostgreSQL. Myślałem o przekształceniu ciągu wejściowego użytkownika w wyrażenie regularne. Teraz użycie ~ * zamiast = lub LIKE działało idealnie! Nie musiałem tworzyć nowych indeksów, kolumn ani nic takiego. Oczywiście wyszukiwanie wyrażeń regularnych jest wolniejsze niż porównywanie bajtów prostych, ale nie sądzę, że wpływ na wydajność byłby o wiele większy niż konieczność obsługi dwóch zestawów danych (jednego małego lub dużego tylko do wyszukiwania, a następnie konieczności pobrania odpowiedniego oryginału dane z drugiego zestawu). Poza tym jest czystsze!
Cyberknight
1
Dobrze, ale jak zrobić na przykład regexp_matches ()?
WKT
Według dokumentów postgres: Operator ~~ jest równoważny LIKE, a ~~ * odpowiada ILIKE. Istnieją również operatory! ~~ i! ~~ *, które reprezentują odpowiednio NOT LIKE i NOT ILIKE. Wszystkie te operatory są specyficzne dla PostgreSQL.
sh4
Napotkałem problem, gdy w tekście znajdują się nawiasy kwadratowe, ponieważ nie działa. jak: „code (LC)”
Oshan Wisumperuma
8

Korzystanie ~*może znacznie poprawić wydajność dzięki funkcjonalności INSTR.

SELECT id FROM groups WHERE name ~* 'adm'

zwraca wiersze o nazwie zawierającej OR równe „adm”.

Robin Goh
źródło
1
Hej, Robin, witamy w SO. Odpowiedź Jamesa Browna już zaproponowała to rozwiązanie. Ponadto proponowana odpowiedź w żaden sposób nie wykorzystuje wyrażenia regularnego.
Rafael