REGEX Wert zwischen zwei Zeichen prüfen?
Ich stehe mit RegEx zugegebenermaßen auf Kriegsfuß. Jetzt möchte ich eine Zahl gegen einen Wert prüfen, der in einer MySQL Tabelle in einer Zelle stehen könnte, in der mehrere Werte, getrennt durch ein Zeichen stehen.
String der geprüft werden soll:
|1|2|3|4|5|6|7|8|12|13|17|20|26|28|38|40|41|66|
Nun möchte ich mit RegEx prüfen, ob z.B. die 2 im String enthalten ist.
Prüfe ich mit
\|(.*?)\|
wird gegen 1 3 5 7 usw. geprüft, also nur gegen jeden 2. Wert. Getestet habe ich das mit https://regex101.com
Mit welcher RegEx kann ich gegen 1 2 3 4 5 6 7 8 12 13 usw. prüfen?
Danke schonmal.
4 Antworten
Nun wie du das mit Regex löst wurde dir ja schon gesagt. Generell läuft hier aber denke ich sehr Vieles falsch.
Zum einen, dass die Daten in dieser Art überhaupt in einer Zelle stehen und du sie nachträglich auseinanderbauen musst. Eine relationale Datenbank ist eben dafür da, Daten atomar zu speichern und ggf. mit entsprechenden JOINs wieder zusammenzufügen.
Weiter eignet sich selbst wenn wir das ignorieren doch SQL selbst Bestens dafür und man kann einfach ein LIKE oder INSTR nutzen. Und genauso sind Funktionen in den Hochsprachen zum finden eines Strings hier angebrachter als ein Regex, der wohl rechenaufwendiger und komplizierter ist.
Also was spricht gegen ein:
SELECT TABELLE.Spalte FROM TABELLE
WHERE TABELLE.DeinString LIKE '%|2|%';
Oder ein:
SELECT TABELLE.Spalte FROM TABELLE
WHERE INSTR(TABELLE.DeinString, '|2|') != 0;
Aber wie gesagt, eigentlich stinkt das schon von den Tabelleninhalten her. Klar gibt es auch hier halbwegs brauchbare Mittel und Wege, wie textbasierte Indizes usw. die damit dann zumindest in Bezug auf Performance keine großen Nachteile bringen.
Danke. Irgendwie war ich aufgrund der Trennung mit Zeichen ('|') in der irrigen Annahme, mit RegEx arbeiten zu müssen. Dein Vorschlag mit %|2|% funktioniert aber gut und ich setze es so um.
Warum ich in einer Zelle mehrere Werte speichere? Ich habe in der db viele Infos zu Mobiltelefonen und für jedes Feature ein Feld. Bei den 2G/3G/4G/5G Bändern/Frequenzen stehen jedoch unterschiedlich viele. Wenn nun ein Nutzer nach den Frequenzen filtern will, die z.B. in seinem (Urlaubs-)Land genutzt werden, um zu sehen, ob sein (noch zu kaufendes) Smartphone diese unterstützt, nutze ich die Query. Bisher hatte ich vieles in PHP realisiert und mir mit MySQL dumm die ganze Tabelle eingelesen. Das war natürlich viel zu langsam. Das baue ich gerade um und Du hast mir dabei sehr geholfen.
\|2\|
Solange du nur einen Match willst klappt das super.
Nur kannst du eben nicht gleichzeitig mehrere Zahlen matchen, heißt bei |1|2|2|4| würdest du nur einen Match bekommen und sowas wie \|(2|3)\| wird nicht funktionieren.
Da helfen dann Lookaheads und Lookbehinds:
(?<=\|)(2|3)(?=\|)
Die haben außerdem den Vorteil, dass das | nicht mit-gematcht wird.
Ja, leider. Die in Go kann zum Beispiel keine Lookaheads/Lookbehinds, größter Witz sowas.
nur genau die 2, aber weder 12, 20, 26 noch 28?
'|2|'
Danke. Ich nutze jetzt "/" als Separatoren in der Datenbank. Da ich ja mit der RegEx \b1\b prüfe, ist offensichtlich der Separator in der db egal. In der Query sollte ich auch mit \b(1|2|3)\b mehrere Werte checken können.
Wenn ich auf 2 prüfen will, dann quasi auf |2|. Wenn ich auf 20 prüfen will, auf |20|. Durch die senkrechten Striche vorher und nachher ist auch eindeutig eingrenzbar, was der geamte zu prüfende Teilstring ist.
Konkret habe ich in einer Tabellenzelle zu stehen, welche LTE oder 5G Kanäle ein Smartphone unterstützt und möchte gezielt eine Tabelle nach Geräten durchsuchen, die z.B. Kanal 2 unterstützen.
Jo, das scheint mir ein brauchbarer Weg, um die Aufgabenstellung enorm zu vereinfachen. Seperatoren dürfen ja durchaus miteinbezogen werden - allerdings willst du sicherstellen, dass sowohl am Anfang als auch am Ende diese Seperatoren stehen.
Nur so kann ich ja sicherstellen, dass gegen die ganze Zahl geprüft wird. Würde ich z.B. den Strich vor der 1 am Anfang weglassen, könnte ich ja gar nicht gegen 11 testen. Jetzt fehlt mir nur noch der passende RegEx ;-)
Um nur die 2 zu matchen:
\b2\b
\b ist eine "word boundary".
Um eine beliebige Zahl zu matchen:
\b(\d+)\b
Ich habe das jetzt in meine MySQL Query eingebaut und leider bekomme ich keinen positiven Match. Meine Query lautet
SELECT * FROM database WHERE 5g REGEXP '\b1\b'
Wo ist mein Denkfehler?
Im Endeffekt würde ich nicht nur einen Vergleich machen, sondern mehrere, also
SELECT * FROM database WHERE 5g REGEXP '\b(1|5|78)\b'
Sicherlich ein ganz blöder Syntaxfehler.
Das Problem ist wahrscheinlich, dass du die Backslashes in MySQL Strings escapen musst:
SELECT * FROM database WHERE 5g REGEXP '\\b(1|5|78)\\b'
immer darauf achten , nicht jede regex bibliothek beherscht alles :)