ABAP select - pattern matching with trailing asterisks

141 Views Asked by At

Say I have a Z table that allows wildcards like this:

enter image description here

So e.g. D123 should have the flag set, D124 matches on D12* so should not have the flag set and D234 matches on D*** so should have the flag set. The asterisks can only be trailing (or ****) so you can't have **2* stored in the table for example.

Then I want to do a select on this table from a list of depots. I could select everything into an internal table and then do pattern matching from that but I was wondering if there was an efficient way to do it e.g. with a single query?

2

There are 2 best solutions below

0
Herr Berthm On

I'm not aware of any way to do pattern matching in reverse like this.

If you don't want to read the entire DB you could create a range of all the posible values that would match. Since you specified that the stars can only be at the end that's not to many entries you're looking for. Something like this should give you a range:

data: lv_depot type char4. "Depot to be searched
data: lr_depot type range of char4. "All Patterns that match the depot

data: lv_len type i.
data: lv_stars type char4.
data: lv_low type char4.

lv_depot = 'D123'.

do strlen( lv_depot ) + 1 times.
  lv_len = strlen( lv_depot ) - sy-index + 1.
  if lv_len > 0.
    lv_low = lv_depot(lv_len) && lv_stars.
  else.
    lv_low = lv_stars.
  endif.

  append value #( sign = 'I' option = 'EQ' low = lv_low ) to lr_depot.
  lv_stars = lv_stars && '*'.
enddo.

When you do the select you can sort the result by depot descending to find the entry with the least stars at the end, since the star comes before letters and numbers (assuming you're only using those for the depot values):

data: ls_test type ztestmk.
select * from ztest into ls_test
    up to 1 rows
    where depot in lr_depot
    order by depot descending.
  exit.
endselect.
0
Michael Koval On

You can reverse the order of selects, first selecting your custom "pattern table" and using that to select the list of matching depots. Group all "depot patterns" by flag, then do two selects. Afterwards set the flag for the two result tables appropriately. Use + to replace * in your custom table to build a range to use for a select, as per documentation.

TYPES: BEGIN OF condition,
         depot TYPE depot,
         flag  TYPE abap_bool,
       END OF condition.

DATA conditions TYPE STANDARD TABLE OF condition WITH EMPTY KEY.
" just as an example, you would select this table
conditions = VALUE #( ( vpsta = 'D***' ) ).

DATA range_flagged TYPE RANGE OF depot.
" builds range table, replacing '*' with '+'
range_flagged = VALUE #(
  FOR condition IN conditions WHERE ( flag = abap_true )
    ( sign = 'I' option = 'CP' low = replace( val = condition-vpsta sub = '*' with = '+' ) ) ).

SELECT depot, flag
  FROM my_depot_table
  INTO TABLE @DATA(depots_flagged)
  WHERE depot IN @range_flagged.

 depots_flagged = VALUE #( FOR depots IN depots_flagged ( flag = abap_true ) ).

DATA range_unflagged TYPE RANGE OF depot.
range_unflagged = VALUE #(
  FOR condition IN conditions WHERE ( flag = abap_false )
    ( sign = 'I' option = 'CP' low = replace( val = condition-vpsta sub = '*' with = '+' ) ) ).

SELECT depot, flag
  FROM my_depot_table
  INTO TABLE @DATA(depots_unflagged)
  WHERE depot IN @range_unflagged.

Afterwards depots_flagged and depots_unflagged contains the depots, flagged according to your custom "pattern table". Replacing * with + in your custom "pattern table" directly would simplify building your range table.