Dette tip er opdateret siden første udgivelse, da en læser har gjort mig opmærksom på, at den orindelige FILTER-MATCH ikke kunne håndtere gentagne forekomster af en tegnstreng fra :WILDCARD i :HAYSTACK. Et eksempel er 'AACB' LIKE '%A_B%', som fejlede i den oprindelige FILTER-MATCH, fordi A optræder to gange, men kun findes een gang af den oprindelige FILTER-MATCH.
For nylig opdagede en af mine kolleger følgende SQL statement i et COBOL program:
SELECT 0 FROM TABLE_WITH_EXACTLY_ONE_ROW WHERE :HAYSTACK LIKE :WILDCARD
Og vedkommende spurgte mig, hvad foregår der her?? Efter at have sundet mig lidt gik det op for mig, at denne stump SQL undersøger om indholdet af hostvariablen :HAYSTACK matcher wildcard værdien i :WILDCARD. Dette SQL kald kan selvfølgelig spares fuldstændig væk, hvis man koder en stump wildcard kode i sit COBOL program. Dette sparer turen over i DB2 og opslaget på tabellen, som DB2 laver uanset, om det er nødvendigt eller ej.
Jeg søgte derefter på internettet efter en stump COBOL-kode, der kunne erstatte SQL kaldet. Jeg kunne ikke umiddelbart finde noget, der præcist simulerer LIKE, så jeg kastede mig ud i opgaven:
...
WORKING-STORAGE SECTION.
*
01 SQLCODE PIC S9(9) BINARY.
01 FILTER-STRING.
49 FILTER-LEN PIC S9(4) BINARY.
49 FILTER PIC X(50).
01 FILTER-POS PIC S9(4) BINARY.
01 MATCH-POS PIC S9(9) BINARY.
01 MATCH.
49 MATCH-LENGTH PIC S9(9) BINARY.
49 MATCH-DATA PIC X(50000).
01 WILDCARD-POS PIC S9(4) BINARY.
01 SUB-LEN PIC S9(4) BINARY.
01 PERCENT PIC X(1).
01 PERCENT-POS PIC S9(4) BINARY.
01 REST-LEN PIC S9(9) BINARY.
01 RESTART-POS PIC S9(9) BINARY.
...
PROCEDURE DIVISION.
MOVE 54 TO MATCH-LENGTH
MOVE 'THIS IS A TEST OF SQL LIKE CODED IN COBOL AND IT WORKS'
TO MATCH-DATA(1:MATCH-LENGTH)
MOVE 14 TO FILTER-LEN
MOVE 'THIS%SQL%WORK_'
TO FILTER(1:FILTER-LEN)
PERFORM FILTER-MATCH
IF SQLCODE = 0
DISPLAY 'IT WAS A MATCH!'
ELSE
DISPLAY 'NO MATCH FOUND'
GOBACK
.
FILTER-MATCH.
MOVE 0 TO SQLCODE
IF FILTER-LEN NOT = MATCH-LENGTH
OR FILTER(1:FILTER-LEN) NOT = MATCH-DATA(1:FILTER-LEN)
IF FILTER(1:FILTER-LEN) = SPACES
OR MATCH-DATA(1:MATCH-LENGTH) = SPACES
MOVE 100 TO SQLCODE
ELSE
MOVE 'N' TO PERCENT
MOVE 0 TO RESTART-POS
MOVE 1 TO FILTER-POS
MOVE 1 TO MATCH-POS
PERFORM UNTIL FILTER-POS > FILTER-LEN
OR MATCH-POS > MATCH-LENGTH
OR SQLCODE = 100
MOVE 1 TO SUB-LEN
IF FILTER(FILTER-POS:1) = '%'
MOVE 'Y' TO PERCENT
MOVE FILTER-POS TO PERCENT-POS
ELSE
IF FILTER(FILTER-POS:1) NOT = '_'
IF PERCENT = 'Y'
COMPUTE WILDCARD-POS = FILTER-POS + 1
PERFORM UNTIL WILDCARD-POS > FILTER-LEN
OR FILTER(WILDCARD-POS:1) = '%' OR '_'
ADD 1 TO WILDCARD-POS
END-PERFORM
COMPUTE SUB-LEN = WILDCARD-POS - FILTER-POS
MOVE MATCH-POS TO RESTART-POS
COMPUTE REST-LEN = MATCH-LENGTH - MATCH-POS + 1
INSPECT
MATCH-DATA(MATCH-POS:REST-LEN)
TALLYING MATCH-POS
FOR CHARACTERS BEFORE
FILTER(FILTER-POS:SUB-LEN)
IF MATCH-POS = RESTART-POS + REST-LEN
MOVE 100 TO SQLCODE
END-IF
END-IF
IF MATCH-DATA(MATCH-POS:SUB-LEN)
NOT = FILTER(FILTER-POS:SUB-LEN)
IF RESTART-POS NOT = 0
MOVE 1 TO SUB-LEN
MOVE RESTART-POS TO MATCH-POS
COMPUTE FILTER-POS = PERCENT-POS - SUB-LEN
MOVE 0 TO RESTART-POS
ELSE
MOVE 100 TO SQLCODE
END-IF
END-IF
MOVE 'N' TO PERCENT
END-IF
ADD SUB-LEN TO MATCH-POS
END-IF
ADD SUB-LEN TO FILTER-POS
END-PERFORM
IF PERCENT = 'N' AND SQLCODE = 0
IF MATCH-POS = MATCH-LENGTH
AND FILTER-POS > FILTER-LEN
OR MATCH-POS > MATCH-LENGTH
AND FILTER-POS = FILTER-LEN
AND FILTER(FILTER-POS:1) NOT = '%'
MOVE 100 TO SQLCODE
END-IF
END-IF
END-IF
END-IF
.
Ovenstående kodestumper er sakset fra det program, jeg skrev, som også indeholder kode til at sammenligne SQL LIKE med ovenstående COBOL paragraf. På den måde var det lettere for mig at sikre, at COBOL koden rent faktisk virker korrekt. Der kan måske stadig forekomme fejl, så brug ovenstående kode med forsigtighed. Bemærk brugen af substring expressions. COBOL er rigtig god til at sluge CPU, hvis der bliver arbejdet på den fulde streng. Resultatet af wildcard sammenligningen gemmes i SQLCODE for at være kompatibel med den SQL som paragraffen erstatter.
Efter at have sikret mig, at funktionaliteten er korrekt (det håber jeg virkelig, den er), kørte jeg nogen performance tests. Paragraffen FILTER-MATCH kører 20 til 30 gange hurtigere end at kalde DB2 for at gøre det samme. I det oprindelige program blev SQL kaldet udført millioner af gange dagligt, så det resulterede i en god besparelse.
En sidste vigtig detalje er, at de to strenge ikke matcher, hvis de ikke indeholder wildcards og ikke er lige lange. det betyder for eksempel, at to blanke strenge, der ikke indeholder lige mange blank-tegn, ikke matcher hinanden. Sådan virker COBOL normalt ikke, men det gør SQL LIKE.