Query table with different list of variables using like

前端 未结 2 1598
孤城傲影
孤城傲影 2021-01-29 11:46

I call a webservice and i get a list of codes: A, B, C, D, E, F etc.

In my problem i get A, B, C.

There\'s a table where you can create sets with the above codes

相关标签:
2条回答
  • 2021-01-29 12:34

    Oracle Setup:

    Start with a simple function to split a delimited string into a collection:

    CREATE OR REPLACE TYPE stringlist AS TABLE OF VARCHAR2(20)
    /
    
    CREATE OR REPLACE FUNCTION split_String(
      i_str    IN  VARCHAR2,
      i_delim  IN  VARCHAR2 DEFAULT ','
    ) RETURN stringlist DETERMINISTIC
    AS
      p_result       stringlist := stringlist();
      p_start        NUMBER(5) := 1;
      p_end          NUMBER(5);
      c_len CONSTANT NUMBER(5) := LENGTH( i_str );
      c_ld  CONSTANT NUMBER(5) := LENGTH( i_delim );
    BEGIN
      IF c_len > 0 THEN
        p_end := INSTR( i_str, i_delim, p_start );
        WHILE p_end > 0 LOOP
          p_result.EXTEND;
          p_result( p_result.COUNT ) := SUBSTR( i_str, p_start, p_end - p_start );
          p_start := p_end + c_ld;
          p_end := INSTR( i_str, i_delim, p_start );
        END LOOP;
        IF p_start <= c_len + 1 THEN
          p_result.EXTEND;
          p_result( p_result.COUNT ) := SUBSTR( i_str, p_start, c_len - p_start + 1 );
        END IF;
      END IF;
      RETURN p_result;
    END;
    /
    

    and some sample data:

    CREATE TABLE your_table( setid, codes, messagedescr ) 
      SELECT 1, 'A,B,C,D', 'You can login' FROM DUAL UNION ALL
      SELECT 2, 'B,C,D',   'You can login for one day' FROM DUAL UNION ALL
      SELECT 3, 'A,C,E',   'You can login but update your profile' FROM DUAL UNION ALL
      SELECT 4, 'B,C,E,F', 'You cannot login' FROM DUAL;
    

    Then you can do (passing your input C,A,B - in any order - as the bind parameter :your_code):

    SELECT *
    FROM   (
      SELECT *
      FROM   your_table
      WHERE  split_string( codes ) SUBMULTISET OF split_String( :your_code )
      ORDER BY setid DESC
    )
    WHERE ROWNUM = 1;
    

    and it will output the row with the highest matching set of codes.

    Note: The above example assumes that you need to match all the codes in the table to codes in your input string. If you only need to match at least one then you can use:

    WHERE split_string( codes ) MULTISET INTERSECT split_String( :your_code ) IS NOT EMPTY
    
    0 讨论(0)
  • 2021-01-29 12:38

    If I understand well your need, this could be a way.

    Say you have a table like this:

    create table yourTable(setid, codes, messagedescr) as ( 
      select 1,       'A, B, C, D',  'You can login' from dual union all
      select 2,       'B, C, D'   ,  'You can login for one day' from dual union all
      select 3,       'A, C, E'   ,  'You can login but update your profile' from dual union all
      select 4,       'B, C, E, F',  'You cannot login' from dual
    ).
    

    This could be a way:

    with inputData(codes) as (
        select listagg(trim (regexp_substr(input_codes, '[^,]+', 1, level))) within group ( order by trim (regexp_substr(input_codes, '[^,]+', 1, level)))
        from ( select 'A, D, C, B' as input_codes from dual )  /* the input string */
        CONNECT BY instr(input_codes, ',', 1, level - 1) > 0
    )    
    select *
    from inputData 
        inner join (
                    select listagg(trim (regexp_substr(codes, '[^,]+', 1, level)))
                             within group ( order by trim (regexp_substr(codes, '[^,]+', 1, level))) as codes,
                            messagedescr
                    from yourTable  
                    CONNECT BY instr(codes, ',', 1, level - 1) > 0
                      and prior setId = setId
                      and prior sys_guid() is not null
                    group by setId, messagedescr
                   )
          using (codes)
    

    The idea here is to split your input string in many rows, and then aggregate the resulting rows in alphabetic order, then apply the same order on the values in table and then check that the ordered strings are equal.

    This part is used to split, order and aggregate the input values, so that the result is an ordered string:

    select listagg(trim (regexp_substr(input_codes, '[^,]+', 1, level))) within group ( order by trim (regexp_substr(input_codes, '[^,]+', 1, level)))
        from ( select 'A, D, C, B' as input_codes from dual )  /* the input string */
        CONNECT BY instr(input_codes, ',', 1, level - 1) > 0
    

    gives:

    ABCD
    

    This part is used to do the same on your table:

    select listagg(trim (regexp_substr(codes, '[^,]+', 1, level)))
             within group ( order by trim (regexp_substr(codes, '[^,]+', 1, level))) as codes,
            messagedescr
    from yourTable  
    CONNECT BY instr(codes, ',', 1, level - 1) > 0
      and prior setId = setId
      and prior sys_guid() is not null
    group by setId, messagedescr  
    

    gives:

    CODES      MESSAGEDESCR
    ---------- -------------------------------------
    ABCD       You can login
    BCD        You can login for one day
    ACE        You can login but update your profile
    BCEF       You cannot login
    

    The join between these partial results is quite straightforward and simply checks if a value (ordered) exists in your table that corresponds to the (ordered) input string.

    0 讨论(0)
提交回复
热议问题