Die regulären Ausdrücke waren schon einmal Thema eines Monatstipps (April 2004), als sie mit der Oracle Version 10g als Neuerung erschienen waren.
Diesmal soll es vor allem um einfache Anwendungsbeispiele für die Funktionen REGEXP_LIKE, REGEXP_SUBSTR und REGEXP_REPLACE gehen.
Wenn man sich mit der gewöhnungsbedürftigen Syntax (einen Überblick finden Sie am Schluss) erst einmal vertraut gemacht hat, können reguläre Ausdrücke beim Formatieren und Bereinigen von Tabelleninhalten, bei der Formulierung von komplizierten Check-Constraints u. ä. sehr gute Dienste leisten.
Ausgangspunkt für die Beispiele ist die folgende Tabelle (s. Anhang), die mit den regexp-Funktionen überprüft, bereinigt und ggf. vor falschen Eingaben geschützt werden sollte:
NAME TELEFON EMAIL PLZ STRASSE
Maier +49/012-345678-012 johann.maier@chaos.de 00000 24 a, Langstr.
meyer 012-345678-013 peter.meyer@chaos.de a0674 1a,Langstr. G
Mayerhöfer 012-345678-014 frank.mayerhoefer@chaos.de 00345 10-11, Langstr.
MEYR 012-345678-015 tomas.meyr@chaos.de 19387 16b, Waldgasse
Mayer 012-345678016 richard.mayer@chaos.de 11028 22, Richterstr.
Müller 012-345678-017 martin.mueller@chaos.de - 235b, Langstr.
Mair 012-345678-018 wilhelmine.mair@chaos.de 12345 35a, Langstr.
meir 0049/012-345678-019 sabine.meir@chaos.de 12345 101, Langstr.
myers 0049-12-345678-029 jeff.myers@chaos.de 03927 10, Schlossallee
müller D012-345678-021 kurt.müller2@chaos.de 12098 10 c, Langstr.
Hintermeier 012-345678-022 hansi.hintermeier@chaos.de 11937 180, Holzweg
MEIER #012-345678023 eva.meier@chaos.de 12346 301, Langstr.
mAYR ?49-12-345678-024 klaus.mayr@chaos.de 19207 5d, Gärtnerplatz
MILLER 345678025 oscar.miller@chaos.de 1525 10 a, Langstr.
REGEXP_LIKE vergleicht den Suchstring mit einem Muster und gibt true oder false zurück.
REGEXP_LIKE(Suchstring, Muster, Match-Parameter)
Die Angabe des Match-Parameters (siehe Anhang) ist optional.
Fangen wir mit der PLZ-Spalte an, in der nur 5 Ziffern stehen sollen und nichts anderes (wenn die PLZ mit einer 0 anfängt, darf an der 2. Stelle keine stehen):
SELECT name, plz, strasse FROM firma
WHERE NOT REGEXP_LIKE(plz, '^0[1-9][0-9]{3}$|^[1-9][0-9]{4}$');
NAME PLZ STRASSE
Maier 00000 24 a, Langstr.
meyer 0674 1a,Langstr.
Mayerhöfer 00345 10-11, Langstr.
Müller - 235b, Langstr.
meir 12345 101, Langstr.
müller 12098 10 c, Langstr.
Hintermeier 11937 180, Holzweg
mAYR 19207 5d, Gärtnerplatz
MILLER 1525 10 a, Langstr.
Erläuterungen:
Die Oracle-Funktion REGEXP_REPLACE ersetzt das Muster im Suchstring durch den Ersatzstring.
REGEXP_REPLACE(Suchstring, Muster, Ersatzstring, Position, Vorkommen, Match-Parameter)
Position: Position im Suchstring, an dem der Mustervergleich anfängt, default ist 1.
Vorkommen: das nte Vorkommen des Musters im Suchstring, default ist 1.
Die Angabe von Position, Vorkommen und Match-Parameter ist optional.
Die störenden Leerzeichen und Buchstaben in der PLZ-Spalte kann mittels REGEXP_REPLACE in einem Rutsch entfernen. (Wenn der Ersatzstring leer bleibt, kann man ihn auch weglassen):
UPDATE firma SET plz = REGEXP_REPLACE(plz, '[^0-9]','')
WHERE NOT REGEXP_LIKE(plz, '^0[1-9][0-9]{3}$|^[1-9][0-9]{4}$'); 9 Zeilen wurden aktualisiert.
Kontrolle:
SELECT name, plz, strasse FROM firma
WHERE not REGEXP_LIKE(plz, '^0[1-9][0-9]{3}$|^[1-9][0-9]{4}$')OR plz IS NULL;
NAME PLZ STRASSE
Maier 00000 24 a, Langstr.
meyer 0674 1a,Langstr.
Mayerhöfer 00345 10-11, Langstr.
Müller 235b, Langstr.
MILLER 1525 10 a, Langstr.
Diese 5 Felder muss man von Hand korrigieren, bevor man einen Check-Constraint einrichten kann:
UPDATE firma SET plz = ... WHERE name = ...
Nach der Korrektur lässt sich durch Hinzufügen eines check-Constraints sicherstellen, dass in Zukunft keine Buchstaben, Sonder- oder Leerzeichen eingegeben werden:
ALTER TABLE firma
ADD CONSTRAINT firma_plz_check
CHECK (REGEXP_LIKE(plz, '^0[1-9][0-9]{3}$|^[1-9][0-9]{4}$'));
Ähnlich funktioniert die Einrichtung eines Check-Constraints der e-mail-Adressen, z.B.:
ALTER TABLE firma
ADD CONSTRAINT firma_mail_check
CHECK(REGEXP_LIKE(email, '^\w+\.\w+@\w+\.de$'));
Wenn man auch Bindestriche oder ähnliches in den e-mail-Adressen zulassen will, muss man das Muster anders formulieren (dafür kann man Name und Vorname in einem Klammerausdruck unterbringen), z. B.:
^[-._0-9a-zA-Z]+@[-_0-9a-zA-Z]+\.[A-Za-z]{2,4}$'
REGEXP_SUBSTR gibt einem dem Muster entsprechenden Teilstring des Suchstrings aus.
Was vor 10g mitunter komplizierte Kombinationen der Funktionen instr, SUBSTR und length erforderte, läßt sich mit REGEXP_SUBSTR in sehr kurzen Ausdrücken bewerkstelligen.
REGEXP_SUBSTR(Suchstring, Muster, Position, Vorkommen, Match-Parameter)
Position: Position im Suchstring, an dem der Mustervergleich anfängt, default ist 1.
Vorkommen: das nte Vorkommen des Musters im Suchstring, default ist 1.
Die Angabe von Position, Vorkommen und Match-Parameter ist wiederum optional.
Die Sekretärin möchte eine Adressenliste der Mitarbeiter als View erstellen, die folgende Informationen enthalten soll: Vorname, Nachname, PLZ, Straße und Hausnummer.
Die Vornamen kann man aus den e-mail-Adressen extrahieren und mit den Nachnamen verknüpfen:
SELECT INITCAP(REGEXP_SUBSTR(email, '[^.]+')||' '||name) AS Name FROM firma;
Aus johann.maier@chaos.de wird somit Johann Maier u.s.w. ...
Die umgedrehte Reihenfolge von Hausnummer und Strasse im amerikanischen Stil läßt sich durch mittels REGEXP_REPLACE umkehren:
SELECT LTRIM(REGEXP_REPLACE(strasse, '([^,]+),([^,]+)','\2 \1'))
AS strasse FROM firma;
Langstr. 24 a
Langstr. 1a
Langstr. 10-11
......
Jetzt können wir die View erstellen:
CREATE OR REPLACE VIEW adressen
AS SELECT
INITCAP(REGEXP_SUBSTR(email, '[^.]+')||' '||name) AS Name, plz,
LTRIM(REGEXP_REPLACE(strasse, '([^,]+),([^,]+)','\2 \1')) AS strasse
FROM firma;
SELECT * FROM adressen;
NAME PLZ STRASSE
Johann Maier 12345 Langstr. 24 a
Peter Meyer 12345 Langstr. 1a
Frank Mayerhöfer 12345 Langstr. 10-11
.......
Die folgende View stellt alle Informationen in getrennten Spalten dar:
CREATE OR REPLACE VIEW adressen
AS SELECT INITCAP(REGEXP_SUBSTR(email, '[^.]+')) AS Vorname,
INITCAP(name) as Nachname, plz,
LTRIM(REGEXP_SUBSTR(strasse, '([^,]+)',1,2)) AS strasse,
LTRIM(REGEXP_SUBSTR(strasse, '([^,]+)')) AS Nr
FROM firma;
Aus einem String wie z.B. '24 a, Langstr.' extrahiert
REGEXP_SUBSTR(strasse, '([^,]+)',1,2))das 2. Vorkommen des Musters (Strasse), und REGEXP_SUBSTR(strasse, '([^,]+)',1,1)) das 1. Vorkommen (Hausnummer, die beiden Einser können hier auch wegfallen, weil sie der Default sind):
SELECT * FROM adressen;
VORNAME NAME PLZ STRASSE NR
Johann Maier 12345 Langstr. 24 a
Peter Meyer 12345 Langstr. 1a
Frank Mayerhöfer 12345 Langstr. 10-11
....
Alle Angestellten, die in der Langstrasse wohnen, möchten eine Fahrgemeinschaft bilden. Der Fahrer hätte gern eine nach Hausnummern geordnete Liste der Namen. Wegen der Buchstaben und Leerzeichen etc. funktioniert der direkte Ansatz nicht, auch wenn man die vorangehenden Leerzeichen mit LTRIM eliminiert:
SELECT name, strasse FROM firma
WHERE strasse LIKE '%Lang%'
ORDER BY LTRIM(strasse);
NAME STRASSE
meyer 1a,Langstr.
MILLER 10 a, Langstr.
müller 10 c, Langstr.
meir 101, Langstr.
Mayerhöfer 10-11, Langstr.
....
Eine Kombination dieser alphabetischen Sortierung mit der Sortierung nach dem Zahlenteilstring bringt dagegen das erwünschte Ergebnis:
SELECT INITCAP(name) Name, LTRIM(REGEXP_SUBSTR(strasse,'[^,]+')) Nr
FROM firma
WHERE strasse LIKE '%Lang%'
ORDER BY TO_NUMBER(REGEXP_SUBSTR(strasse, '[0-9]+')), LTRIM (strasse);
NAME NR
Meyer 1a
Miller 10 a
Müller 10 c
Mayerhöfer 10-11
....
REGEXP_SUBSTR(strasse, '[0-9]+') extrahiert die erste zusammenhängende Zahl aus dem String,(z.B. 10-11 ' 10) was die Umwandlung mit TO_NUMBER und damit die richtige Sortierung ermöglicht.
Die Ausgabe kann man noch etwas verschönern, indem man die doppelten Leerzeichen in der Hausnummern-Spalte entfernt:
SELECT INITCAP(name) Name,
LTRIM(REGEXP_REPLACE(REGEXP_SUBSTR(strasse,'[^,]+'),'( ){2,}',
' ')) Nr
FROM firma
WHERE strasse LIKE '%Lang%'
ORDER BY TO_NUMBER(REGEXP_SUBSTR(strasse, '[0-9]+')), LTRIM (strasse);
NAME NR
Meyer 1a
Miller 10 a
Müller 10 c
....
Die Telefonnummern sollen von Leer-, Sonderzeichen u. ä. befreit werden und folgendes Format erhalten: (+49-12)345-6789-029. Die zu bereinigenden Einträge findet man mit:
SELECT telefon FROM firma WHERE REGEXP_LIKE(telefon, '[^-0-9]');
TELEFON
+49/012-345678-012
0049/012-345678-019
0049-12-345678-029
D012-345678-021
012-345678-022
#012-345678023
?49-12-345678-024
[^-0-9] steht für alles außer Bindestrich und Ziffern. Mit REGEXP_REPLACE kann man so alle Zeichen außer den Ziffern entfernen, indem man den Ersatzstring einfach wegläßt.:
UPDATE firma SET telefon = REGEXP_REPLACE(telefon, '[^0-9]');
14 Zeilen wurden aktualisiert
Für die Formatierung gibt es 2 Möglichkeiten:
a) Verwendung der Oracle-SUBSTR-Funktion (weil sich nur die Durchwahl ändert):
UPDATE firma SET telefon = '(+49-12)345-678-' || SUBSTR(telefon,-3);
SUBSTR(telefon,-3) startet beim dritten Zeichen von hinten und gibt den Rest aus.
b) Verwendung von REGEXP_REPLACE, komplizierter, aber allgemeingültiger:
UPDATE firma SET telefon = REGEXP_REPLACE(telefon,
'(0*49)?(0?12)?(\d{3})(\d{3})(\d{3})', '(+49-12)\3-\4-\5');
Gruppe: 1 2 3 4 5
Hier werden wieder Backreferences verwendet. Die Gruppen von Zeichen, die man umformatieren will, werden durch runde Klammern gekennzeichnet.
Die Gruppen 1 (beliebig viele Nullen gefolgt von 49) und 2 (eine oder keine Null gefolgt von 12) die jeweils ein oder kein Mal vorkommen, werden durch (+49-12) ersetzt, die folgenden 3 Gruppen 3 - 5 mit Bindestrich aneinandergehängt.
Die Säuberung und Formatierung kann man mit einem geschachtelten REGEXP_REPLACE auch in einem Zug erledigen:
ROLLBACK;
UPDATE firma SET telefon =
REGEXP_REPLACE(REGEXP_REPLACE(telefon,'[^0-9]'),
'(0*49)?(0?12)?(\d{3})(\d{3})(\d{3})','(+49-12)\3-\4-\5');
SELECT telefon FROM FIRMA;
TELEFON
(+49-12)345-678-012
(+49-12)345-678-013
(+49-12)345-678-014
(+49-12)345-678-015
Angenommen, die Tabelle hätte 100000 Datensätze und wir wollen alle Angestellten suchen, die Maier oder so ähnlich heißen, unabhängig von Groß- und Kleinschreibung und Schreibweise:
SELECT name FROM firma WHERE REGEXP_LIKE (name, '^m[ae][yi].?r$','i');
Eine Alternative dazu ist die Oracle-SOUNDEX-Funktion, die es schon vor 10g gab:
SELECT INITCAP(name) FROM firma
WHERE SOUNDEX(name) LIKE SOUNDEX('Maier');
NAME
Maier
Meyer
Meyr
Mayer
Mair
Meir
Meier
Mayr
Außerhalb von eckigen Klammern bzw. in Kombinationen mit Charakterklassen muss man den Metazeichen einen Backslash voranstellen, wenn man explizit nach ihnen sucht,
z.B. \+, \?, \* etc.
Mit dem folgenden SQL-Code können Sie sich die Übungstabelle selbst erstellen:
DROP TABLE firma;
CREATE TABLE firma(
name VARCHAR2(20),
telefon VARCHAR2(20),
email VARCHAR2(30),
plz VARCHAR2(10),
strasse VARCHAR2(50));
INSERT INTO firma VALUES
('Maier','+49/012-345678-012','johann.maier@chaos.de','00000','24 a,Langstr.');
INSERT INTO firma VALUES
('meyer','012-345678-013','peter.meyer@chaos.de','a0674','1a,Langstr. ');
INSERT INTO firma VALUES'
('Mayerhöfer','012-345678-014','frank.mayerhoefer@chaos.de','00345','10-11,Langstr.');
INSERT INTO firma VALUES
('MEYR','012-345678-015','tomas.meyr@chaos.de','19387','16b,Waldgasse');
INSERT INTO firma VALUES
('Mayer','012-345678016','richard.mayer@chaos.de','11028',' 22,Richterstr.');
INSERT INTO firma VALUES
('Müller','012-345678-017','martin.mueller@chaos.de','-','235b,Langstr.');
INSERT INTO firma VALUES
('Mair','012-345678-018','wilhelmine.mair@chaos.de','12345',' 35a,Langstr.');
INSERT INTO firma VALUES
('meir','0049/012-345678-019','sabine.meir@chaos.de',' 12345','101,Langstr.');
INSERT INTO firma VALUES
('myers','0049-12-345678-029 ','jeff.myers@chaos.de','03927','10,Schlossallee');
INSERT INTO firma VALUES
('müller','D012-345678-021','kurt.müller2@chaos.de','12098 ','10 c,Langstr.');
INSERT INTO firma VALUES
('Hintermeier',' 012-345678-022','hansi.hintermeier@chaos.de',' 11937','180,Holzweg ');
INSERT INTO firma VALUES
('MEIER','#012-345678023','eva.meier@chaos.de','12346','301,Langstr.');
INSERT INTO firma VALUES
('mAYR','?49-12-345678-024','klaus.mayr@chaos.de',' 19207','5d,Gärtnerplatz ');
INSERT INTO firma VALUES
('MILLER','345678025','oscar.miller@chaos.de','1525','10 a,Langstr.');
COMMIT;
In unserer Know-How Datenbank finden Sie mehr als 300 ausführliche Beiträge zu den
Oracle-Themen wie DBA, SQL, PL/SQL, APEX und vielem mehr.
Hier erhalten Sie Antworten auf Ihre Fragen.