PostgreSQL: come eseguire query "senza distinzione tra maiuscole e minuscole"


339

Esiste un modo per scrivere query senza distinzione tra maiuscole e minuscole in PostgreSQL, ad esempio voglio che le seguenti 3 query restituiscano lo stesso risultato.

SELECT id FROM groups where name='administrator'

SELECT id FROM groups where name='ADMINISTRATOR'

SELECT id FROM groups where name='Administrator'

se citext viene fornito con l'installazione di Postgres, prova il tipo citext. È un testo senza distinzione tra maiuscole e minuscole
Michael Buen,

2
Per i nuovi arrivati ​​a questa domanda, questo link alla documentazione ufficiale di postgres contiene tutte le risposte fornite qui, nonché alcune altre opzioni.
Parthian Shot

Per favore, riassegnate la risposta accettata a quella fatta da @Arun. È molto meno complicato e non tirare un sacco di problemi dopo l'applicazione.
zeliboba,

Risposte:


451

Utilizzare la funzione INFERIORE per convertire le stringhe in lettere minuscole prima del confronto.

Prova questo:

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

92
È importante notare che l'uso di INFERIORE (o qualsiasi altra funzione) sulle colonne del predicato - in questo caso "nome" - non renderà più possibile la ricerca di alcun indice. Se si tratta di una tabella di grandi dimensioni o con una query frequente, ciò potrebbe causare problemi. Fascicolazione senza maiuscole / minuscole, citext o un indice basato su funzioni miglioreranno le prestazioni.
Giordania,

108
O semplicemente creare un indice come questo: CREATE INDEX idx_groups_name ON gruppi inferiore (nome);
Daniel,

19
Specificare anche varchar_pattern_opsse si desidera che l'indice funzioni con la LIKE 'xxx%'query, ad es CREATE INDEX ix_groups_name ON groups (lower(name) varchar_pattern_ops).
Sayap

10
L'uso dell'operatore ILIKE (come mostrato nelle altre risposte di seguito) è un approccio più semplice, anche se questa è la risposta più votata.
Ryan,

5
Passando attraverso i commenti qui, molti suggerimenti qui suggeriscono ILIKEche funzionerà but with slow response. Per ottenere un rapido accesso alle tabelle in base ai risultati dei calcoli, suggerisco a chiunque stia semplicemente controllando che ciò vada con la risposta accettata. Vedi maggiori dettagli qui e qui
Afolabi Olaoluwa Akinwumi,

231

usando ILIKEinvece diLIKE

SELECT id FROM groups WHERE name ILIKE 'Administrator'

1
Si noti che ILIKEnon è supportato da Hibernate quando utilizzato in Spring Boot.
AnT

@AnT funziona con org.hibernate.dialect.PostgreSQL94DialectSpring Boot 2.0.6.RELEASE. Ma IntelliJ se ne lamenta.
Samintha Kaveesh

134

L'approccio più comune è quello di minuscole o maiuscole della stringa di ricerca e dei dati. Ma ci sono due problemi con questo.

  1. Funziona in inglese, ma non in tutte le lingue. (Forse nemmeno nella maggior parte delle lingue.) Non tutte le lettere minuscole hanno una lettera maiuscola corrispondente; non tutte le lettere maiuscole hanno una lettera minuscola corrispondente.
  2. L'uso di funzioni come lower () e upper () ti darà una scansione sequenziale. Non può usare gli indici. Sul mio sistema di test, l'utilizzo di lower () richiede circa 2000 volte di più di una query che può utilizzare un indice. (I dati del test hanno poco più di 100.000 righe.)

Esistono almeno tre soluzioni utilizzate meno frequentemente che potrebbero essere più efficaci.

  1. Utilizzare il modulo citext , che imita principalmente il comportamento di un tipo di dati senza distinzione tra maiuscole e minuscole. Dopo aver caricato quel modulo, puoi creare un indice senza distinzione tra maiuscole e minuscole CREATE INDEX ON groups (name::citext);. (Ma vedi sotto.)
  2. Utilizzare una raccolta senza distinzione tra maiuscole e minuscole. Viene impostato quando si inizializza un database. L'uso di regole di confronto senza distinzione tra maiuscole e minuscole significa che puoi accettare praticamente qualsiasi formato dal codice client e otterrai comunque risultati utili. (Significa anche che non puoi fare domande con distinzione tra maiuscole e minuscole. Duh.)
  3. Crea un indice funzionale. Creare un indice minuscolo utilizzando CREATE INDEX ON groups (LOWER(name));. Fatto ciò, puoi sfruttare l'indice con query come SELECT id FROM groups WHERE LOWER(name) = LOWER('ADMINISTRATOR');, o SELECT id FROM groups WHERE LOWER(name) = 'administrator';Devi ricordare di usare LOWER (), comunque.

Il modulo citext non fornisce un vero tipo di dati senza distinzione tra maiuscole e minuscole. Al contrario, si comporta come se ogni stringa fosse in minuscolo. Cioè, si comporta come se avessi chiamato lower()ogni stringa, come nel numero 3 sopra. Il vantaggio è che i programmatori non devono ricordarsi di stringhe minuscole. Ma devi leggere le sezioni "Comportamento del confronto di stringhe" e "Limitazioni" nei documenti prima di decidere di usare citext.


1
Circa # 1: non dovrebbe essere un problema, dal momento che sarebbero due stringhe diverse (pensaci come fare col = 'a'e col = 'b'). Circa # 2: come hai detto, puoi creare un indice su un'espressione, quindi non è davvero un problema. Ma sono d'accordo con te sul fatto che cambiare la collation è molto probabilmente la soluzione migliore.
Vincent Savard,

5
Qualcuno può dirmi quali regole di confronto insensibili al maiuscolo / minuscolo sono le regole di confronto integrate in PostgreSQL? Lo vedo come un'opzione, ma non riesci a trovare nulla su una raccolta senza distinzione tra maiuscole e minuscole per Postgres in rete?
khorvat,

1
@AnupShah: No, non lo sto dicendo. Non eseguo PostgreSQL su Windows. I documenti 9.4 dicono questo : "Su tutte le piattaforme, sono disponibili le regole di confronto denominate default, C e POSIX. Potrebbero essere disponibili regole di confronto aggiuntive a seconda del supporto del sistema operativo." Puoi vedere con quali collisioni PostgreSQL pensa siano disponibili select * from pg_collation;.
Mike Sherrill "Cat Recall",

1
@Matthieu: questa è la migliore introduzione (e attenzione) all'argomento che conosco: Edge Cases to Keep in Mind. Parte 1 - Testo .
Mike Sherrill 'Cat Recall',


95

È possibile utilizzare ILIKE. vale a dire

SELECT id FROM groups where name ILIKE 'administrator'

È corretto e funziona bene per me, sto usando MAC OS X (Mountain Lion).
ADJ

5
Funzionerà, ma con una risposta lenta. Per ottenere un rapido accesso alle tabelle in base ai risultati dei calcoli, suggerisco di utilizzare la lowerfunzione. Vedi altri dettagli
Afolabi Olaoluwa Akinwumi,

1
@AfolabiOlaoluwaAkinwumi fondamentalmente questo dipende dal fatto che tu stia cercando risultati invece di filtrare valori noti . In quest'ultimo caso, un singolo caso uniforme dovrebbe essere mantenuto a livello di dati consentendo all'operatore di uguaglianza di lavorare. [La raccomandazione personale è in maiuscolo per i valori del codice tipo]
Chris Marisic,

53

Puoi anche leggere la ILIKEparola chiave. Può essere abbastanza utile a volte, sebbene non sia conforme allo standard SQL. Vedi qui per maggiori informazioni: http://www.postgresql.org/docs/9.2/static/functions-matching.html


9
Qualcosa a cui prestare attenzione qui è l'input di utenti malintenzionati. Se si esegue una query come email ILIKE 'user-input-email-here', assicurarsi di sfuggire all'input dell'utente. Altrimenti le persone possono inserire caratteri come% che corrispondono a qualsiasi cosa.
Matt De Leon,

2
@MattDeLeon Hi. Ben detto. Ma voglio solo chiederti se uso ILIKEe prepared statementsquesto mi proteggerà sql injection?
slevin,

Non sono sicuro, suppongo che tu voglia inviare una stringa di escape all'istruzione preparata.
Matt De Leon,

1
"La parola chiave ILIKE può essere utilizzata al posto di LIKE per rendere la corrispondenza senza distinzione tra maiuscole e minuscole in base alla locale attiva. Questo non è nello standard SQL ma è un'estensione PostgreSQL." Funziona come un incantesimo nel 9.3
Aleksey Deryagin il

1
ILIKE è più lento di lower(column_name) like %expression%.
Patryk Imosa,

28

Puoi anche usare espressioni regolari POSIX, come

SELECT id FROM groups where name ~* 'administrator'

SELECT 'asd' ~* 'AsD' ritorna t


1
Ho avuto lo stesso problema, avevo bisogno di ricerche senza distinzione tra maiuscole e minuscole sul mio database PostgreSQL. Ho pensato di trasformare la stringa di input dell'utente in un'espressione regolare. Ora, usare ~ * invece di = o LIKE ha funzionato perfettamente! Non avevo bisogno di creare nuovi indici, colonne o altro. Certo, la ricerca di regex è più lenta del confronto di byte semplici, ma non penso che l'impatto sulle prestazioni sarebbe molto maggiore rispetto alla necessità di gestire due set di dati (uno inferiore o maiuscolo solo per la ricerca, quindi è necessario recuperare l'originale corrispondente dati dall'altro set). Inoltre, questo è più pulito!
Cyberknight,

1
Bene, ma come fare ad esempio con regexp_matches ()?
WKT,

Secondo i documenti di postgres: l'operatore ~~ equivale a LIKE e ~~ * corrisponde a ILIKE. Ci sono anche operatori! ~~ e! ~~ * che rappresentano rispettivamente LIKE e NOT ILIKE. Tutti questi operatori sono specifici di PostgreSQL.
sh4,

Ho riscontrato un problema quando le parentesi sono incluse nel testo, non funziona. come: "code (LC)"
Oshan Wisumperuma,

8

L'utilizzo ~*può migliorare notevolmente le prestazioni, con la funzionalità di INSTR.

SELECT id FROM groups WHERE name ~* 'adm'

restituisce righe con nome che contiene OR è uguale a "adm".


1
Ehi, Robin, benvenuto in SO. La risposta di James Brown ha già proposto questa soluzione. Inoltre, la risposta proposta non sfrutta in alcun modo regex.
Rafael,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.