Home > front end >  Oracle SQL : Check if specified words are present in comma separated string
Oracle SQL : Check if specified words are present in comma separated string

Time:05-09

I have an SQL function that returns me a string of comma separated country codes.

I have configured some specific codes in another table and I may remove or add more later.

I want to check if the comma separated string is only the combination of those specific country codes or not. That said, if that string is having even a single country code other than the specified ones, it should return true.

Suppose I configured two rows in the static data table GB and CH. Then I need below results:

String from function    --- result
GB                      false 
CH                      false
GB,CH                   false
CH,GB                   false
GB,FR                   true
FR,ES                   true
ES,CH                   true
CH,GB,ES                true

I am on Oracle 19c and can use only the functions available for this version. Plus I want it to be optimised. Like I can check the number of values in string and then count for each specific code. If not matching then obviously some other codes are present. But I don't want to use loops.

Can someone please suggest me a better option.

CodePudding user response:

You can convert a csv column to a table and use EXISTS. For example

with tbl(id,str) as
( 
SELECT 1,'GB,CH' FROM DUAL UNION ALL
SELECT 2,'GB,CH,FR' FROM DUAL UNION ALL
SELECT 3,'GB' FROM DUAL 
),
countries (code) as
(SELECT 'GB' FROM DUAL UNION ALL
 SELECT 'CH' FROM DUAL 
)

select t.* ,
     case when exists (
        select 1 
        from xmltable(('"' || REPLACE(str, ',', '","') || '"')) s  
        where trim(s.column_value) not in (select code from countries)
      ) 
      then 'true' else 'false' end flag
from tbl t

CodePudding user response:

One option is to match the country codes one by one, and then determine whether there exists an extra non-matched country from the provided literal as parameter.

The following one with FULL JOIN would help by considering the logic above

WITH
  FUNCTION with_function(i_countries VARCHAR2) RETURN VARCHAR2 IS
    o_val VARCHAR2(10);
  BEGIN
     SELECT CASE WHEN SUM(NVL2(t.country_code,0,1))=0 THEN 'false' 
                 ELSE 'true'
                  END
       INTO o_val           
       FROM (SELECT DISTINCT REGEXP_SUBSTR(i_countries,'[^ ,] ',1,level) AS country
               FROM dual
            CONNECT BY level <= REGEXP_COUNT(i_countries,',') 1) tt
       FULL JOIN t
              ON tt.country = t.country_code;      
    RETURN o_val;
  END;
SELECT with_function(<comma-seperated-parameter-list>) AS result
  FROM dual

Demo

CodePudding user response:

Assuming that all country codes in the static table, as well as all tokens in the comma-separated strings, are always exactly two-letter strings, you could do something like this:

with
  static_data(country_code) as (
    select 'GB' from dual union all
    select 'CH' from dual
  )
, sample_inputs(string_from_function) as (
    select 'GB'       from dual union all
    select 'CH'       from dual union all
    select 'GB,CH'    from dual union all
    select 'CH,GB'    from dual union all
    select 'GB,FR'    from dual union all
    select 'FR,ES'    from dual union all
    select 'ES,CH'    from dual union all
    select 'CH,GB,ES' from dual
  )
select string_from_function,
       case when regexp_replace(string_from_function,
                   ',| |' || (select listagg(country_code, '|')
                                       within group (order by null)
                              from   static_data))
                 is null then 'false' else 'true' end as result
from   sample_inputs
;

Output:

STRING_FROM_FUNCTION   RESULT  
---------------------- --------
GB                     false   
CH                     false   
GB,CH                  false   
CH,GB                  false   
GB,FR                  true    
FR,ES                  true    
ES,CH                  true    
CH,GB,ES               true

The regular expression replaces comma, space, and every two-letter country code from the static data table with null. If the result of the whole thing is null, then all coded in the csv are in the static table; that's what you need to test for.

The assumptions guarantee that a token like GBCH (for a country like "Great Barrier Country Heat") would not be mistakenly considered OK because GB and CH are OK separately.

CodePudding user response:

Here is one solution

with cte as
(select distinct
s,regexp_substr(s, '[^,] ',1, level) code from strings
    
 connect by regexp_substr(s, '[^,] ', 1, level) is not null
)
select 
s string,min(case when exists
  (select * from countries
   where cod = code) then 'yes'
   else 'no'end) all_found
from cte
group by s
order by s;
STRING | ALL_FOUND
:----- | :--------
CH     | yes      
CH,GB  | yes      
ES     | no       
ES,CH  | no       
FR     | no       
GB     | yes      
GB,CH  | yes      
GB,ES  | no       

db<>fiddle here

CodePudding user response:

If you have a small number of values in the static table then the simplest method may not be to split the values from the function but to generate all combinations of values from the static table using:

SELECT SUBSTR(SYS_CONNECT_BY_PATH(value, ','), 2) AS combination
FROM   static_table
CONNECT BY NOCYCLE PRIOR value != value;

Which, for the sample data:

CREATE TABLE static_table(value) AS
SELECT 'GB' FROM DUAL UNION ALL
SELECT 'CH' FROM DUAL;

Outputs:

COMBINATION
GB
GB,CH
CH
CH,GB

Then you can use a simple CASE expression to your string output to the combinations:

SELECT function_value,
       CASE
       WHEN function_value IN (SELECT SUBSTR(SYS_CONNECT_BY_PATH(value, ','), 2)
                               FROM   static_table
                               CONNECT BY NOCYCLE PRIOR value != value)
       THEN 'false'
       ELSE 'true'
       END AS not_matched
FROM   string_from_function;

Which, for the sample data:

CREATE TABLE string_from_function(function_value) AS
SELECT 'GB' FROM DUAL UNION ALL
SELECT 'CH' FROM DUAL UNION ALL
SELECT 'GB,CH' FROM DUAL UNION ALL
SELECT 'CH,GB' FROM DUAL UNION ALL
SELECT 'GB,FR' FROM DUAL UNION ALL
SELECT 'FR,ES' FROM DUAL UNION ALL
SELECT 'ES,CH' FROM DUAL UNION ALL
SELECT 'CH,GB,ES' FROM DUAL;

Outputs:

FUNCTION_VALUE NOT_MATCHED
GB false
CH false
GB,CH false
CH,GB false
GB,FR true
FR,ES true
ES,CH true
CH,GB,ES true

db<>fiddle here

  • Related