Home > Mobile >  Oracle query to divide rows into delimiter
Oracle query to divide rows into delimiter

Time:12-05

I have data as below.

Attribute operator value
col1 = 02
col2 In ('001','002')
col3 <> A
col4 Not In ('test1','test2','test3')

i am trying to bring this into below format.

col1=02 AND col2=001 OR col2=002 AND col3<>A AND col4<>test1 AND col4<>test2 AND col4<>test3

Could you please suggest oracle SELECT query.

Thanks in advance.

CodePudding user response:

If you want to check how Oracle interprets the predicate, simple create the query with the WHEREclause having your predicate.

The table with the corresponding columns must exists but may be empty.

Then call explain plan and look at the result - check the filter in Predicate Information

EXPLAIN PLAN  SET STATEMENT_ID = 'jara1' into   plan_table  FOR
select * from tab
where 
col1 = 02 and
col2 in ('001','002') and
col3 <> 'A' and
col4 not in ('test1','test2','test3');

get result (cut for brevity)

SELECT * FROM table(DBMS_XPLAN.DISPLAY('plan_table', 'jara1','ALL'));

...
Predicate Information (identified by operation id):
---------------------------------------------------
 
   1 - filter("COL1"=02 AND ("COL2"='001' OR "COL2"='002') AND 
              "COL3"<>'A' AND "COL4"<>'test1' AND "COL4"<>'test2' AND "COL4"<>'test3')

CodePudding user response:

You can write a function:

CREATE FUNCTION parse_filters
RETURN CLOB
IS
  v_sql  CLOB := EMPTY_CLOB();
  v_expr CLOB;
  c_in_value_pattern VARCHAR2(60)
    := '('
       || '\d \.\d*|\.\d '    -- Number literal
       || '|'
       || '''([^'']|'''')*''' -- String literal
       || '|'
       || '".*?"'             -- Quoted identifier
       || '|'
       || '[A-Z][A-Z0-9_$#]*' -- Identifier
       || '),?';

  PROCEDURE append_expr(
    p_sql           IN OUT CLOB,
    p_expr          IN     CLOB,
    p_oper          IN     VARCHAR2,
    p_wrap          IN     BOOLEAN DEFAULT FALSE
  )
  IS
    v_is_start CONSTANT BOOLEAN := LENGTH(p_sql) = 0;
  BEGIN
    IF NOT v_is_start THEN
      p_sql := p_sql || ' ';
    END IF;
    IF NOT v_is_start THEN
      p_sql := p_sql || p_oper || ' ';
    END IF;
    IF p_wrap THEN
      p_sql := p_sql || '(' || p_expr || ')';
    ELSE
      p_sql := p_sql || p_expr;
    END IF;
  END;
BEGIN
  FOR f IN (SELECT attribute, operator, value FROM filters)
  LOOP
    IF f.operator IN ('=', '<>', '!=') THEN
      v_expr := EMPTY_CLOB() || f.attribute || ' ' || f.operator || ' ' || f.value;
      append_expr(v_sql, v_expr, 'AND');
    ELSIF LOWER(f.operator) = 'in' THEN
      DECLARE
        v_list FILTERS.VALUE%TYPE := SUBSTR(f.value, 2, LENGTH(f.value) - 2);
        v_term FILTERS.VALUE%TYPE;
        v_idx  PLS_INTEGER := 1;
        v_temp CLOB;
      BEGIN
        v_expr := EMPTY_CLOB();
        LOOP
          v_term := REGEXP_SUBSTR(f.value, c_in_value_pattern, 1, v_idx, 'i', 1);
          EXIT WHEN v_term IS NULL;
          v_temp := EMPTY_CLOB() || f.attribute || ' = ' || v_term;
          append_expr(v_expr, v_temp, 'OR');
          v_idx := v_idx   1;
        END LOOP;
        append_expr(v_sql, v_expr, 'AND', TRUE);
      END;
    ELSIF LOWER(f.operator) = 'not in' THEN
      DECLARE
        v_list FILTERS.VALUE%TYPE := SUBSTR(f.value, 2, LENGTH(f.value) - 2);
        v_term FILTERS.VALUE%TYPE;
        v_idx  PLS_INTEGER := 1;
      BEGIN
        LOOP
          v_term := REGEXP_SUBSTR(f.value, c_in_value_pattern, 1, v_idx, 'i', 1);
          EXIT WHEN v_term IS NULL;
          v_expr := EMPTY_CLOB() || f.attribute || ' <> ' || v_term;
          append_expr(v_sql, v_expr, 'AND');
          v_idx := v_idx   1;
        END LOOP;
      END;
    END IF;
  END LOOP;
  RETURN v_sql;
END parse_filters;
/

For your sample data:

CREATE TABLE filters ( Attribute, operator, value ) AS
SELECT 'col1', '=',      '''02''' FROM DUAL UNION ALL
SELECT 'col2', 'In',     '(''001'',''002'')' FROM DUAL UNION ALL
SELECT 'col3', '<>',     '''A''' FROM DUAL UNION ALL
SELECT 'col4', 'Not In', '(''test1'',''test2'',''test3'')' FROM DUAL;

Then:

SELECT parse_filters() FROM DUAL;

Outputs:

PARSE_FILTERS()
col1 = '02' AND (col2 = '001' OR col2 = '002') AND col3 <> 'A' AND col4 <> 'test1' AND col4 <> 'test2' AND col4 <> 'test3'

db<>fiddle here

  • Related