Come impostare Sqlite3 in modo che non faccia distinzione tra maiuscole e minuscole durante il confronto delle stringhe?


305

Voglio selezionare i record dal database sqlite3 mediante la corrispondenza delle stringhe. Ma se uso '=' nella clausola where, ho scoperto che sqlite3 fa distinzione tra maiuscole e minuscole. Qualcuno può dirmi come utilizzare il confronto di stringhe senza distinzione tra maiuscole e minuscole?

Risposte:


493

È possibile utilizzare COLLATE NOCASEnella SELECTquery:

SELECT * FROM ... WHERE name = 'someone' COLLATE NOCASE

Inoltre, in SQLite, è possibile indicare che una colonna non dovrebbe fare distinzione tra maiuscole e minuscole quando si crea la tabella specificando collate nocasenella definizione della colonna (le altre opzioni sono binary(impostazione predefinita) e rtrim; vedere qui ). Puoi specificare anche collate nocasequando crei un indice. Per esempio:

crea tabella Test
(
  Text_Value text collate nocase
);

inserire nei valori di Test ('A');
inserire nei valori di Test ('b');
inserire nei valori di Test ('C');

crea indice Test_Text_Value_Index
  on Test (Text_Value collate nocase);

Le espressioni che coinvolgono Test.Text_Valuedovrebbero ora non distinguere tra maiuscole e minuscole. Per esempio:

sqlite> seleziona Text_Value da Test dove Text_Value = 'B';
TEXT_VALUE      
----------------
B               

sqlite> seleziona Text_Value da Test order by Text_Value;
TEXT_VALUE      
----------------
UN               
B               
C    

sqlite> seleziona Text_Value da Test order by Text_Value desc;
TEXT_VALUE      
----------------
C               
B               
UN               

L'ottimizzatore può anche potenzialmente utilizzare l'indice per la ricerca senza distinzione tra maiuscole e minuscole sulla colonna. Puoi verificarlo usando il explaincomando SQL, ad es .:

sqlite> spiega selezionare Text_Value da Test dove Text_Value = 'b';
codice operativo aggiuntivo p1 p2 p3                               
---------------- -------------- ---------- ---------- ---------------------------------
0 Vai a 0 16                                           
1 numero intero 0 0                                            
2 OpenRead 1 3 keyinfo (1, NOCASE)                
3 SetNumColumns 1 2                                            
4 stringhe 8 0 0 b                                
5 IsNull -1 14                                           
6 MakeRecord 1 0 a                                
7 MemStore 0 0                                            
8 MoveGe 1 14                                           
9 MemLoad 0 0                                            
10 IdxGE 1 14 +                                
11 Colonna 1 0                                            
12 Richiamata 1 0                                            
13 Avanti 1 9                                            
14 Chiudi 1 0                                            
15 Halt 0 0                                            
16 Transazione 0 0                                            
17 VerifyCookie 0 4                                            
18 Vai a 0 1                                            
19 Noop 0 0                                            

20
Dopo aver (ri) creato la tabella con 'COLLATE NOCASE', ho notato che era molto più veloce della query WHERE name = 'qualcuno' COLLATE NOCASE. MOLTO più veloce (da sei a 10 volte, all'incirca?)
DefenestrationDay

10
Secondo la documentazione, l'aggiunta COLLATE NOCASEall'indice non è richiesta se il campo stesso ha già definito queste regole di confronto: " La sequenza di confronto predefinita è la sequenza di confronto definita per quella colonna
nell'istruzione

29
COLLATE NOCASEfunzionerà solo con testo ASCII. Una volta che hai "FIANCÉ" o "voilà" nei valori della colonna, non si abbinano a "fidanzato" o "VOILA". Dopo aver abilitato l'estensione ICU, non LIKEfa distinzione tra maiuscole e minuscole , quindi 'FIANCÉ' LIKE 'fiancé'è vero, ma 'VOILA' LIKE 'voilà'è ancora falso. E ICU + LIKE ha lo svantaggio di non utilizzare l'indice, quindi può essere lento su grandi tavoli.

seleziona div, case quando div = 'fail' quindi 'FAIL' altrimenti 'PASSED' fine, * dai segni di fascicolazione nocase sopra non ha funzionato sto facendo qualcosa di sbagliato?
Tuono,

7
Una cosa da notare che mi ha fatto inciampare: non select * from tbl where firstname='john' and lastname='doe' COLLATE NOCASEfarà distinzione tra maiuscole e minuscole lastname. Per essere case insensitive su firstname, scrivere questo: select * from tbl where firstname='john' COLLATE NOCASE and lastname='doe'. È specifico per quella colonna, non per l'intera whereclausola.
James Toomey,

148
SELECT * FROM ... WHERE name = 'someone' COLLATE NOCASE

5
Se sei come me e vuoi più documentazione su Fascicolazione, puoi trovarla qui in questa pagina: sqlite.org/datatype3.html Basta scorrere fino a # 6.0
Will

47

Puoi farlo in questo modo:

SELECT * FROM ... WHERE name LIKE 'someone'

(Non è la soluzione, ma in alcuni casi è molto conveniente)

"L' operatore LIKE esegue un confronto di corrispondenza del modello. L'operando a destra contiene il modello, l'operando di sinistra contiene la stringa da abbinare al modello. Un simbolo percentuale ("% ") nel modello corrisponde a qualsiasi sequenza di zero o più caratteri nella stringa. Un carattere di sottolineatura ("_") nel modello corrisponde a qualsiasi singolo carattere nella stringa. Qualsiasi altro carattere corrisponde a se stesso o al suo equivalente maiuscolo / minuscolo (ovvero corrispondenza senza maiuscole / minuscole ) . (Un bug: SQLite comprende solo maiuscole / minuscole per i caratteri ASCII. L'operatore LIKE distingue tra maiuscole e minuscole per i caratteri unicode che vanno oltre l'intervallo ASCII. Ad esempio, l'espressione 'a' LIKE 'A' è TRUE ma 'æ' LIKE 'Æ'è falso.)."


@ MM-BB sì, a meno che non eseguiamo il LIKE su una colonna dichiarata (o indicizzata) come COLLATE NOCASE, eseguirà una scansione completa delle righe.
Nick Dandoulakis,

1
Non è un bug, è una limitazione documentata. La stessa pagina citata nella risposta menziona l'estensione ICU che gestisce i caratteri unicode. (Forse non è stato il caso nel 2009)
stenci,

40

Questo non è specifico per sqlite ma puoi semplicemente farlo

SELECT * FROM ... WHERE UPPER(name) = UPPER('someone')

L'altra parte del problema delle prestazioni è trovare le righe corrispondenti nella tabella. SQLite3 supporta gli indici basati su funzioni? Indicizzare la colonna o l'espressione di ricerca (ad es. "UPPER (nome)") in una situazione come questa di solito è una buona idea.
cheduardo,

13
Fai attenzione a questo, come ha suggerito Cheduardo, SQLite non può utilizzare un indice su "nome" quando esegue questa query. Il motore db dovrà eseguire la scansione completa di tutte le righe, convertendo tutti i campi "nome" in lettere maiuscole ed eseguendo il confronto.
Mathew Waters,

1
@quantity, sì, molto.
Berga,

4

Un'altra opzione è quella di creare le tue regole di confronto personalizzate. È quindi possibile impostare tale confronto sulla colonna o aggiungerlo alle clausole select. Sarà utilizzato per ordini e confronti.

Questo può essere usato per rendere 'VOILA' COME 'voilà'.

http://www.sqlite.org/capi3ref.html#sqlite3_create_collation

La funzione di confronto deve restituire un numero intero negativo, zero o positivo se la prima stringa è rispettivamente minore, uguale o maggiore della seconda.


2

Un'altra opzione che potrebbe avere o meno senso nel tuo caso è quella di avere effettivamente una colonna separata con valori pre-scontati della colonna esistente. Questo può essere popolato usando la funzione SQLite LOWER()e puoi invece eseguire la corrispondenza su questa colonna.

Ovviamente, aggiunge ridondanza e un potenziale incoerenza, ma se i tuoi dati sono statici potrebbe essere un'opzione adatta.


2

Semplicemente, puoi utilizzare COLLATE NOCASE nella tua query SELECT:

SELECT * FROM ... WHERE name = 'someone' COLLATE NOCASE

1

Se la colonna è di tipo, charallora devi aggiungere il valore che stai interrogando con spazi, fai riferimento a questa domanda qui . Questo oltre a utilizzare COLLATE NOCASEo una delle altre soluzioni (upper (), ecc.).


0

è possibile utilizzare la query simile per confrontare la rispettiva stringa con i valori della tabella.

seleziona il nome della colonna da table_name dove il nome della colonna è come 'rispettivo valore di confronto';


Ciò non aggiungere nulla a stackoverflow.com/a/973665/2462516 che è stato pubblicato nel 2009
umasudhan

0

Funziona perfettamente per me. SELECT NAME FROM TABLE_NAME WHERE NAME = 'test Name' COLLATE NOCASE

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.