Emulieren Sie das REGEXP-ähnliche Verhalten in DB2 SQL

7

Ich habe dasselbe auf stackoverflow gepostet (bitte lassen Sie mich wissen, wenn ich einen löschen muss).

Ich arbeite an einer DB2-Datenbank und soweit ich sehen kann, wird regulärer Ausdruck nicht unterstützt (ohne zusätzliche Bibliotheken).

Daher kann ich nichts Ähnliches implementieren, wie in diesem Artikel " Bringing the Power of Regular Expression Matching to SQL " erläutert.

Wissen Sie, ob ich mit einer SQL-Anweisung einen regulären Ausdruck wie diesen "emulieren" kann?

^ a [aofdmep] {1} [az] {1} [a-z0-9] {4} [sidbfkfpo] {1}

Bearbeiten

In der obigen Hypothese fand ich, dass es für meinen Fall ein WHERE Like-Prädikat akzeptabel ist :

WO USER_NAME NICHT WIE 'a_______'

Aber es ist unsicher und deckt keine anderen Fälle ab, in denen ich kein festes Zeichen habe, mit dem ich übereinstimmen kann.

tmow
quelle
Vielleicht möchten Sie sich diesen Artikel ansehen: Gedanken zu regulären Ausdrücken
vettipayyan
1
Dies mag also tatsächlich in der Klasse der Antworten liegen, aber ich bin mir nicht sicher: In SqlServer würden wir dies über eine CLR-Regex-Komponente tun, und die DB2-Engine scheint die Verwendung von Java auf die gleiche Weise zu ermöglichen. Warum können Sie dies nicht in ein ordnungsgemäß geschriebenes, sehr schlankes Java-JAR umwandeln (das wahrscheinlich in einem Szenario mit engen Schleifen verwendet wird)?
Jcolebrand

Antworten:

11

Zunächst einmal sind die {1}s redundant, also ist es wirklich nur:

^[aofdmep][a-z][a-z0-9]{4}a[sidbfkfpo]

Dies ist eigentlich ein ziemlich einfaches Muster ... es geht nur um die ersten 8 Zeichen der Zeichenfolge, und es ist immer eine Zeichenfolge mit fester Länge, sodass Sie möglicherweise eine Tabelle mit allen Permutationen erstellen und dann Folgendes tun können:

WHERE SUBSTR(string_to_match, 1, 8) IN (
  SELECT valid_prefixes FROM table_of_things_to_match
)

Leider sind es 7 * 26 * 36 ^ 4 * 9 ... 2,75 Milliarden mögliche Kombinationen, aber immer noch feste Zeichenfolgen, so dass Sie Folgendes tun können:

WHERE SUBSTR(string_to_match, 1, 1) IN ('a','o','f','d','m','e','p')
  AND SUBSTR(string_to_match, 2, 1) IN ('a','b','c','d' ... 'z')
  AND SUBSTR(string_to_match, 3, 1) IN ('a','b','c','d' ... 'z','0','1' ... '9')
  AND SUBSTR(string_to_match, 4, 1) IN ('a','b','c','d' ... 'z','0','1' ... '9')
  AND SUBSTR(string_to_match, 5, 1) IN ('a','b','c','d' ... 'z','0','1' ... '9')
  AND SUBSTR(string_to_match, 6, 1) IN ('a','b','c','d' ... 'z','0','1' ... '9')
  AND SUBSTR(string_to_match, 7, 1) = 'a'
  AND SUBSTR(string_to_match, 8, 1) IN ('s','i','d','b','f','k','p','o')

(Füllen Sie die ...Bits natürlich aus)

oops, es gibt zwei fs in dieser letzten Charakterklasse, also gibt es nur 2,45 Milliarden Permutationen.

Ich werde nicht so tun, als ob es schnell gehen würde ... es wird wahrscheinlich nicht so sein, aber es wird dir das Muster bringen, nach dem du suchst. Wenn Sie dazu neigen, dies häufig zu tun, würde ich wahrscheinlich eine Zeichentabelle erstellen, damit Sie auf einfache Weise alphanumerisch oder alphanumerisch auswählen können.

Joe
quelle
+1. Gute Option, vielen Dank! Ich werde ein bisschen auf eine andere Antwort warten, wenn überhaupt.
Tmow
Schließlich habe ich nach über 2 Jahren entschieden, dass Ihre Antwort die richtigste und sauberste ist. Danke!
Tmow
1
Nachdem ich mir das 6+ Jahre später angesehen habe, weil es eine positive Bewertung bekommen hat ... wie zum Teufel habe ich die Bewegung avon Position 1 auf Position 7 verschoben, als ich das {1}s entfernt habe?
Joe
Ja, stimmt, aber immer noch die richtige Antwort.
Tmow
11

Da diese alte Frage ohnehin ausgegraben wurde, möchte ich erwähnen, dass Sie die in DB2 integrierte XQuery-Unterstützung für den Abgleich regulärer Ausdrücke verwenden können

select whatever
from users
where
   xmlcast(
     xmlquery('fn:matches($USER_NAME,"^a[aofdmep][a-z][a-z0-9]{4}[sidbfkfpo]")') 
     as integer) = 1

XMLQUERYoben ruft die XQuery- matchesFunktion für die Spalte auf USER_NAME. Das Ergebnis ist ein XML-Code boolean, mit dem XMLCASTes in einen SQL-Datentyp konvertiert wird.

mustaccio
quelle
Ich versuche, einen Spaltennamen einzugeben, in dem Sie $ USER_NAME haben, aber behalten Sie diesen Fehler bei . Bitte kommentieren.
Lonnie Best
@LonnieBest bitte eine separate Frage stellen. Stellen Sie sicher, dass Sie genügend Details angeben, nicht nur ein Bild einer Fehlermeldung.
Mustaccio
Hier ist meine Frage .
Lonnie Best
6
SELECT * FROM
   (SELECT 'afr923zs' MyString FROM SYSIBM.SYSDUMMY1)
WHERE substr(MyString,1,1) = 'a' 
AND   substr(MyString,2,1) IN ('a','o','f','d','m','e','p')
AND   substr(MyString,3,1) BETWEEN 'a' AND 'z'
AND  (substr(MyString,4,1) BETWEEN 'a' AND 'z' 
   OR substr(MyString,4,1) BETWEEN '0' AND '9')
AND  (substr(MyString,5,1) BETWEEN 'a' AND 'z' 
   OR substr(MyString,5,1) BETWEEN '0' AND '9')
AND  (substr(MyString,6,1) BETWEEN 'a' AND 'z' 
   OR substr(MyString,6,1) BETWEEN '0' AND '9')
AND  (substr(MyString,7,1) BETWEEN 'a' AND 'z' 
   OR substr(MyString,7,1) BETWEEN '0' AND '9')
AND   substr(MyString,8,1) IN ('s','i','d','b','f','k','p','o');
Leigh Riffel
quelle
Vielen Dank! Genial wie Joe, aber einfacher.
Tmow
Ich überprüfe, ob es auf DB2 funktioniert und wie man es schließlich
ändert
1
Die DUAL-Tabelle ist in DB2 nicht vorhanden, es ist jedoch möglich, SYSIBM.SYSDUMMY1 zu verwenden.
Tmow
2

Aufbauend auf den Antworten von Leigh Riffel und Joe könnten Sie LIKE in Betracht ziehen, wenn Sie eine lange Liste einzelner Zeichen haben oder wenn Sie mehrere Zeichenbereiche haben.

SELECT *
  FROM (SELECT 'afr923zs' MyString FROM SYSIBM.SYSDUMMY1) T
 WHERE substr(MyString,1,1) = 'a'
   AND 'aofdmep' like '%'||substr(MyString,2,1)||'%'
   AND substr(MyString,3,1) BETWEEN 'a' AND 'z'
   AND 'abcdefghijklmnopqrstuvwxyz0123456789' like '%'||substr(MyString,4,1)||'%'
   AND 'abcdefghijklmnopqrstuvwxyz0123456789' like '%'||substr(MyString,5,1)||'%'
   AND 'abcdefghijklmnopqrstuvwxyz0123456789' like '%'||substr(MyString,6,1)||'%'
   AND 'abcdefghijklmnopqrstuvwxyz0123456789' like '%'||substr(MyString,7,1)||'%'
   AND 'sidbfkpo' like '%'||substr(MyString,8,1)||'%'
;

Da Sie dieselbe Zeichenliste mehrmals verwenden, können Sie auch eine konstante Spalte CROSS JOINed verwenden.

SELECT *
  FROM (SELECT 'afr923zs' MyString FROM SYSIBM.SYSDUMMY1) T
  CROSS JOIN (SELECT 'abcdefghijklmnopqrstuvwxyz0123456789' alphanum FROM SYSIBM.SYSDUMMY1) T2
 WHERE substr(MyString,1,1) = 'a'
   AND 'aofdmep' like '%'||substr(MyString,2,1)||'%'
   AND substr(MyString,3,1) BETWEEN 'a' AND 'z'
   AND alphanum like '%'||substr(MyString,4,1)||'%'
   AND alphanum like '%'||substr(MyString,5,1)||'%'
   AND alphanum like '%'||substr(MyString,6,1)||'%'
   AND alphanum like '%'||substr(MyString,7,1)||'%'
   AND 'sidbfkpo' like '%'||substr(MyString,8,1)||'%'
;

Für Ihr Beispiel nicht erforderlich, aber die CROSS JOINed "Tabelle" könnte mehrere benannte Zeichenklassenspalten definieren.

Dbenham
quelle
0

In DB2 für z / OS würde SQL PASSING wie folgt enthalten

select whatever
from users
where
   xmlcast(
     xmlquery('fn:matches($USER_NAME,"^a[aofdmep][a-z][a-z0-9]{4}[sidbfkfpo]")'
      PASSING USER as "USER_NAME") 
     as integer) = 1
Aidanh
quelle