Umlaute bei Regular Expressions

Hallo,
wie muss ich PostgreSQL 8.1.1 konfigurieren, dass die Abfrage
SELECT * FROM adressen WHERE name ~* ‘müller’;
sowohl “Müller” als auch “MÜLLER” findet?

Ich habe die Datenbank mit UTF8-Encoding konfiguriert und auch das local environment auf de_DE.UTF8 eingestellt; Betriebssystem ist SuSE-Linux 10.0.

Vielen Dank für eure Hilfe,
Helmar

[quote=“helmar”]Hallo,
wie muss ich PostgreSQL 8.1.1 konfigurieren, dass die Abfrage
SELECT * FROM adressen WHERE name ~* ‘müller’]

Hallo Helmar,

konfigurieren muss Du gar nichts, sondern nur den Befehle richtig schreiben. Im deutsche Manual Kapitel 9.6 “Mustervergleiche” , im englischen Manual 9.7. “Pattern Matching”.

Was die Zeichen “~*” aussagen sollen kann ich nicht deuten.

Ansonsten heißt der Befehl: SELECT * FROM adressen WHERE upper (name) = ‘MÜLLER’;


mfg

fristo

Hallo Fristo,

ja, wenn ich auf regular expressions verzichten kann, reicht mir die WHERE-Clause mit der Funktion upper(). In meinem Fall möchte ich aber auch “Müller-Meisenpiep” und “Schwarzmüller” mit abdecken, und da fand ich den Einsatz von regular expressions sehr verlockend - zumal das englische Manual im Kapitel 9.7.3 (POSIX Regular Expressions) den Operator “~*” dafür vorschlägt (matches regular expressions, case insensitive).

Wenn ich das alles richtig verstanden habe, wird bei diesem Vergleich auf die Zuordnung von Groß- und Kleinbuchstaben zueinander in den Funktionen im Umfeld von LC_CTYPE zurückgegrifen (englisches Manual 21.1.1). Ich meine, dass ich bei mir alles, was dort als Konfigurationsrichtlinien angegeben war, berücksichtigt habe.

Trotzdem funktioniert’s bei mir nicht - und ein Ausdruck wie
~* ‘m(ü|Ü)ller’ kostet nach meiner Erfahrung auf die Dauer ziemlich viel Rechenleistung (besonders wenn mehrere Umlaute im Suchmuster auftreten).

Schöne Grüße,
Helmar

Nochmal zu Fristo

ich habe inzwischen noch ein wenig herumexperimentiert. Wenn ich auf regular expressions verzichten kann, könnte ich mein Problem mit der WHERE-Clause
WHERE upper(name) LIKE ‘%MÜLLER%’
erschlagen.

Trotzdem würde mich doch mal interessieren, warum “~*” nicht funktioniert bzw. was ich tun muss, um es zum Funktionieren zu bringen - allein schon aus der Faulheit heraus, meinen Code nicht nach diesen Operatoren durchscannen zu müssen :wink:

Schöne Grüße,
Helmar

Hallo Helmar,

Das wäre auch meine Antwort gewesen, wenn Du Deine Frage am Anfang vollständig formuliert hättest.

Du kannst eine zusätzliche Spalte “name_upper” anlegen, wo die Namen alle groß geschrieben sind. Dadurch ist die Funktion upper(name) in der WHERE-Clausel nicht mehr notwendig und die Suchgeschwindigkeit wird erhöht.


Mit dem Thema “Regular Expression” solltest Du Dich mal sehr intensiv auseinander setzen. Es sieht am Anfang sehr kompliziert aus, ist es aber nicht. Wenn man es erstmal begriffen hat, kann viele Suchprobleme auf die einfachte Art lösen!



mfg

fristo

Moin,

mit

SELECT * FROM adressen WHERE name ~* ‘.müller.’;

wird er aber “MÜLLER” nicht finden.

Oder hab ich das falsch verstanden, das er mit einer suche nach “müller” sowohl “MÜLLER” als auch “müller” finden will?

Ich hab die erfahrung gemacht, das nur die gefunden werden wo die Umlaute übereinstimmen, also groß oder klein geschrieben sind ebenso wie das suchmuster aber nicht beide.

Obwohl das suchen nach “upper(…)” auch für mich eine neue idee ist :wink:

Crypi

moin,

müller <> MÜLLER <> MüLlEr …

aber mit dem UPPER kann man das umgehen. man kann ja auch direkt in die tabelle eine spalte UPPER(namensfeld) AS NAMEGRS oder sowas einfügen und dann geht das dynamisch und du kannst hier vergleichen.

hat ausserdem den vorteil das die datenbank das vorbereiten kann und nicht bei jeden select neu machen muss…

cu tb

habs grad mal mit upper() ausprobiert… klapp nicht…

WHERE
upper(a.name) ~~* ‘%müller%’

findet auch wirklich nur “müller” und nicht “MÜLLER”

Crypi

ähm klar. schau dir mal genau an was du hier machst… wenn du die spalte mit upper bearbeitest machst du alles groß. dann ist schon klar das in deinem vergleich kein müller sein darf sondern MÜLLER…

SELECT * FROM adressen WHERE UPPER(name) ~* ‘..MÜLLER.’;

cu tb.

select upper(name) from kontakt;

liefert

upper

MÜLLER
MüLLER


zur info, das eine war ‘MÜLLER’ und das andere ‘müller’

Crypi

hi,

hab gerade folgendes gefunden:

Die Lokalisierungseinstellung locale=de DE ist erforderlich fur eine korrekte Sortierung und Konversion (upper, lower) von Umlauten und kann nur auf der Serverseite zum Zeitpunkt von initdb eingestellt werden!
Siehe dazu den Abschnitt Localization im PostgreSQL Administrators Guide.

im handbuch (http://www.postgresql.org/docs/8.1/interactive/charset.html) findest du beim punkt 21.1.2 auch noch was…

cu tb.

jo hab ich auch grad gefunden…

bin grad am probieren.

Crypi

Aber denkt dran, de_DE reicht möglicherweise nicht aus; man braucht eventuell auch noch den charset (z.B. UTF8).

Wäre toll, wenn ihr herausfindet, wie man “~*” zum Laufen kriegt - ich mag regular expressions…

Helmar

~* funktioniert doch gut…
muss man nix für zum laufen bringen… nur eben bei umlauten klappts nicht.


Crypi

Ja, aber genau das isses ja!!!
~* ist absolut super, habe es schon beliebig oft verwendet und macht (weil ich bislang noch keine Beschwerden bekommen hatte) bei Latin-1 offenbar auch bei Umlauten keine Probleme.

Nur nach der Umstellung auf UTF8 (seufz) ging der Ärger los…

Kennt irgendjemand einen DB-Guru, der sich mit regular expressions und UTF8 auskennt?

Übrigens: Bei mir funktioniert upper(‘müller’) so wie erwartet; wenn das bei jemandem (Crypi) nicht funktioniert hat, liegt das wahrscheinlich am Aufruf von initdb und dem zu diesem Zeitpunkt aktuellen Stand von LC_CTYPE.

Gruß
Helmar

Hallo

ist ja hoch interessant was man hier lesen kann, was alles nicht funktionieren soll und warum es nicht funktionieren soll.

Zur Klärung mal folgendes:

  1. Ich habe postgresql-8.1.2-1 auf Windows XP installiert. Der Klient ist PGAdmin. Die Locale in postgresql.conf sind entsprechend dem Betriebssystem auf ‘German_Germany.28591’ eingestellt. Die Datenbank mit der ich arbeite hat als Kodierung Latin1.

  2. Alle von mir hier vorgeschlagenen Befehle funktionieren einwandfrei und sind mit entsprechenden Daten aus meiner Datenbank getestet worden.

  3. Bevor man vorschnell behauptet, das die von mir hier angegegben Befehle nicht funktionieren, sollte man die Befehl mittels eines Funktionstest erst einmal überprüfen.

  4. Bevor ich hier noch etwas weiter kommentiere, hätte ich von Euch die Ergebnisse des Funktionstest der Befehle “select ‘XXXMÜLLERYyyy’ ~* ‘.müller.’;” und “select upper (‘müller’);”. Wer es nicht weiß, so etwas macht man im Query-Tool.

Dann warte ich mal auf Eure Antworten.



mfg

fristo

Hallo Fristo,

bei der Kodierung Latin1 habe auch ich bislang keinerlei Probleme bekommen. Ich denke, die Ursache ist UTF8.

Zu deinen Abfragen (bei mir allerdings micht mit PGAdmin sondern mit einfachstem psql):

postgres=# select ‘XXXMÜLLERYyyy’ ~* ‘.müller.’;
?column?

f
(1 Zeile)

postgres=# select upper (‘müller’);
upper

MÜLLER
(1 Zeile)


Also exakt das Verhalten, das meine Applikation erwarten lässt.

In meinem postgresql.conf stehen die Locale auf den Defaultwerten des Linuxsystems: de_DE.UTF-8.

Schöne Grüße,
Helmar

Hallo helmar,

zwischenzeitlich habe ich unter Windows PostgreSQL neu installiert. Die Datenbank hat jetzt die Kodieurng UTF-8 und client_encoding hat den Wert latin1. Die Locale ist auf ‘German_Germany.1252’ eingestellt.

Ich habe eine Tabelle erzeugt und Daten, verschiedene Varianten von ‘Müller’, mittel INSERT INTO … eingefügt. Anschließend SELECT * FROM adressen WHERE name ~* ‘.müller.’; ausgeführt. Das Ergebnis war positiv. Alle ‘Müller’-Varianten wurden gefunden. Als bei mir kein Problem mit UTF-8.


\

[quote=“helmar”]Zu deinen Abfragen (bei mir allerdings micht mit PGAdmin sondern mit einfachstem psql):

postgres=# select ‘XXXMÜLLERYyyy’ ~* ‘.müller.’]
Dieses Ergebnis hatte ich allerdings beim Funktionstest nicht erwartet, insbesondere da in der Doku auch keine Notiz vorhanden ist, die eine Einschränkung von Regular Expressions beschreibt. Allerdings kenne ich derartige Fehler bei Regular Expression Libraries, die nicht für UniCode ausgelegt sind. Aber für PostgreSQl ist das schon eigenartig.

Nur Schade, dass kein anderer, der eine PostgreSQL Datenbank mit UTF-8 unter LINUX betreibt, den vorgeschlagenden Test macht und das Ergebnis hier mal mitteilt. Dann könnte man aus der Ergebnissen einige Rückschlüsse ziehen.


mfg

fristo

Hallo Crypi

Unter Windows funktioniert UTF-8 einschließlich Regular Expressions einwandfrei. Das habe ich jetzt getestet.

Haste vielleicht die client_encoding auf ASCII eingestellt?



mfg

fristo

Hallo zusammen,

hab leider im Augenblick nicht so ganz viel Zeit übrig, um das genau zu testen, hätte aber vielleicht einen kleinen Hinweis bzw. Frage an die Leute, die sich da vielleicht ein wenig besser auskennen:

In der Postgres-backend-Datei (habe momentan nur die Quellen von 8.1.0 zur Hand…) src/backend/regex/regc_locale.c wird für die Funktion pg_wc_toupper(pg_wchar) zurückgegriffen auf die Systemfunktion toupper(unsigned char). Entsprechendes gilt auch für die Funktion pg_wc_tolower.

Meiner Meinung nach ist dies für UTF8-Zeichen, die außerhalb der ASCII-Codierung liegen, problematisch, da diese mit Sicherheit nicht mehr als unsigned char angesehen werden können.

Statt dessen wären towupper(wint_t) bzw. towlower(wint_t) wohl die angemessenen Funktionen, zumal die auch wirklich auf LC_CTYPE Rücksicht nehmen.

Ich werde in den nächsten Tagen testweise regc_locale.c entsprechend ändern - falls nicht jemand anderes eher etwas Zeit dafür erübrigen kann oder mir erklärt, dass ich total auf dem Holzweg bin - und von den Ergebnissen berichten. Von Risiken und Nebenwirkungen habe ich natürlich keine Ahnung…

Gruß,
Helmar