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.