Verwendung von COLLATE in Android SQLite - Locales wird in LIKE-Anweisung ignoriert
Frage
Wenn in Android ich meine SQLite Datenbank erstellen stellen Sie die Datenbank locale - db.setLocale (neue Locale ( "cz_CZ")). Dies ist eine tschechische locale.
Ein SELECT-Anweisung funktioniert und nimmt das Gebietsschema berücksichtigt, zum Beispiel:
SELECT * from table WHERE name='sctzy' COLLATE LOCALIZED
Wird der Eintrag finden 'šctžý'.
Aber mit LIKE wird fehlschlagen:
SELECT * from table WHERE name LIKE '%sctzy%' COLLATE LOCALIZED
Keine Zeile zurückgegeben wird.
BTW. Es gibt keine java.text.Normalized Klasse in Android. Ich dachte, ich eine zweite Säule mit einem normalisierten Text machen könnte, von Sonderzeichen beraubt, die für die Suche verwendet werden würde - aber ich bin fehle eine Klasse oder Art und Weise, wie der String zu normalisieren
.Lösung
Haben Sie auf der einen Blick hatte SQLite Dokumentation für LIKE ? Es ist gekommen, Informationen über nicht-ASCII-Zeichen und einen Fehler. Vielleicht Android hat eine ältere Version von SQLite installiert, wo dies ein Problem ist.
ich denke, die zweite normalisierte Spalte der beste Option leider sein könnte.
Andere Tipps
eine zweite normalisierte Spalte erstellen können verwendet werden, Einschränkungen zu umgehen (wie kurz erwähnt in anderen Antworten).
In der Praxis bedeutet, dass Sie eine andere (Schatten) Spalte Ihres ersten zu schaffen, wo die gleichen Daten in einem festen Gehäuse (zum Beispiel all oberen Zeichen) gespeichert ist. Groß- und Kleinschreibung Abfragen (einschließlich ähnlichen Abfragen) können auf diese neue Spalte mit Suchwert im gleichen Fall vorgenommen werden.
Wenn die erste Spalte "a" enthält
AAA
aaa
Bbb
ÄÄÄ
ééé
Die zweite Spalte a_shadow enthalten würde für den gleichen Reihen
AAA
AAA
BBB
ÄÄÄ
ÉÉÉ
und Ihre ursprüngliche Abfrage (Beispiel) "wählen Sie eine aus Mytable wobei a = 'AAA'"
würde mit "wählen Sie eine von mytable wo A = 'AAA'" ersetzt
muss Ihr Code aktualisiert werden, um den umgewandelte Schatten Inhalt zu füllen, wenn der primären Inhalt hinzufügen. Wenn die Spalte nach der Erstellung hinzugefügt wird, oder Sie können den Code nicht vorhandene Werte können sich ändern müssen eine Aktualisierungsabfrage zu konvertierenden verwenden. Beispiel:
UPDATE mytable SET a_shadow=UPPER(a);
Gerade heute hatte ich genau die gleiche Aufgabe wie Sie hatten. Und in meiner Situation zusätzliche Schatten Spalten macht, ist kein Fall, weil ich mehr als eine Spalte zu suchen habe. So kam ich auf die Lösung wie diese, die in realem Projekt getestet wird. In meinem Fall bin ich der Handhabung nur Kleinbuchstaben, aber Sie können die Funktion mit Großbuchstaben als auch erweitern.
db.setLocale(Locale("cz", "CZ"))
val query = "SELECT * FROM table WHERE name GLOB ${getExpr(str)} ORDER BY name COLLATE LOCALIZED ASC"
private fun getExpr(input: String) : String{
var expr = ""
for(lettter in input){
expr += when(lettter){
's','š' -> "[sš]"
'a','á' -> "[aá]"
'e','ě','é' -> "[eěé]"
'i','í' -> "[ií]"
'z','ž' -> "[zž]"
'c','č' -> "[cč]"
'y','ý' -> "[yý]"
'r','ř' -> "[rř]"
'u','ů','ú' -> "[uůú]"
'o','ó' -> "[oó]"
'n','ň' -> "[nň]"
'd','ď' -> "[dď]"
't','ť' -> "[tť]"
else -> lettter
}
}
return "'*${expr}*'"
}
könnte zeitaufwändig sein, aber man kann die java.text.Normalizer wie hier verwendet
Symbole umwandeln, Accent Letters to Englisch Alphabet
Wie ist nicht Teil der Java-Untergruppe, dass Android, Sie können versuchen, den Code von Java um es zu suchen, wie Normalizer.java Mit dem Javadoc gefunden hier :
Und kopieren Sie den Teil des Codes in Ihrem Projekt benötigt werden.
Hope es funktioniert!
In Android SQLite, LIKE
und GLOB
ignorieren sowohl COLLATE LOCALIZED
und COLLATE UNICODE
(sie nur Arbeit für ORDER BY
). Wie jedoch @asat erklärt in href="https://stackoverflow.com/a/53535409/6766261">, können Sie GLOB
mit einem Muster verwenden, die jeden Buchstaben mit allen verfügbaren ersetzen Alternativen dieses Schreibens. In Java:
public static String addTildeOptions(String searchText) {
return searchText.toLowerCase()
.replaceAll("[aáàäâã]", "\\[aáàäâã\\]")
.replaceAll("[eéèëê]", "\\[eéèëê\\]")
.replaceAll("[iíìî]", "\\[iíìî\\]")
.replaceAll("[oóòöôõ]", "\\[oóòöôõ\\]")
.replaceAll("[uúùüû]", "\\[uúùüû\\]")
.replace("*", "[*]")
.replace("?", "[?]");
}
Und dann (nicht wörtlich wie folgt, natürlich):
SELECT * from table WHERE lower(column) GLOB "*addTildeOptions(searchText)*"
Auf diese Weise beispielsweise in Spanisch, einem Benutzer der Suche nach entweder mas oder más wird die Suche in m erhalten umgewandelt [Aaaaaa] s , beide Ergebnisse zurück.
Es ist wichtig zu bemerken, dass GLOB
COLLATE NOCASE
ignoriert, deshalb habe ich alles in Kleinbuchstaben sowohl in der Funktion und in der Abfrage. Beachten Sie auch, dass die lower()
Funktion in SQLite nicht funktioniert auf Nicht-ASCII-Zeichen - aber auch hier diejenigen sind wahrscheinlich diejenigen, die Sie ersetzen bereits
Die Funktion ersetzt auch beide GLOB
Platzhalter, *
und ?
, mit "entkommen" Versionen.