Динамическое условие where в Oracle PL SQL

#oracle #dynamic #plsql

#Oracle #динамическое #plsql

Вопрос:

Мне нужно создать динамический выбор таким образом

 fielda = 'Y';
fieldB = 'N';
fieldC = 'Y';

select * from tablea
where tablea.id = 9999
if (fielda = 'Y')
   and tablea.a = 'XXX'   --add this condition 
End if 
if (fieldb = 'Y')
   and tablea.b = 'YYY'   -- not add this condition 
End if
if (fieldc = 'Y')
   and tablea.c = 1234    --add this condition 
End if
...
...
 

Результат выбора должен быть

 select * from tablea
where tablea.id = 9999
and tablea.a = 'XXX'   
and tablea.c = 1234    
 

Теперь я должен создать это

 sqlstm := 'select * from tablea';
sqlstm := sqlstm || ' where tablea.id = 9999 ';
if (fielda = 'Y')
   sqlstm := sqlstm || ' and tablea.a = '''XXX''' '
End if 
blah blah blah

execute immediate sqlstm
 

Но знаете ли вы способ сделать это без «sqlstm бла-бла-бла» и «выполнить немедленно»?

С уважением, Марко

Ответ №1:

Вы можете использовать OR AND подобные условия и, учитывая, что значения полей будут равны Y или N.

 select * from tablea
 where tablea.id = 9999
   And ( (fielda = 'N' OR tablea.a = 'XXX')
        AND (fieldb = 'N' OR tablea.b = 'YYY')
        AND (fieldc = 'N' OR tablea.c = 1234)
      )
 

Комментарии:

1. Извините, я должен объяснить лучше

2. Теперь смотрите обновленный ответ. Также вместо fielda = 'N' вы можете написать fielda != 'Y' в соответствии с вашим требованием.

Ответ №2:

Если у вас есть только несколько возможных условий, я бы также предложил не использовать динамический SQL. Однако, если у вас много полей и / или они могут отображаться в любой комбинации, динамическое решение может быть таким:

 DECLARE
    sqlstr VARCHAR2(30000);
    curId NUMBER := DBMS_SQL.OPEN_CURSOR;
    ret INTEGER;
    refCur SYS_REFCURSOR;
BEGIN

    sqlstr := 'select * from tablea where tablea.id = 9999 ';
    IF (fielda = 'Y') THEN
        sqlstr := sqlstr 'AND tablea.a = :p1 ';
    END IF; 
    IF (fieldb = 'Y') THEN
        sqlstr := sqlstr 'AND tablea.b = :p2 ';
    END IF;
    IF (fieldc = 'Y') THEN
         sqlstr := sqlstr 'AND tablea.c = :p3 '; 
    END IF;
   DBMS_SQL.PARSE(curid, sql_stmt, DBMS_SQL.NATIVE);

    IF (fielda = 'Y') THEN
        DBMS_SQL.BIND_VARIABLE(curid, ':p1', 'XXX');
    END IF; 
    IF (fieldb = 'Y') THEN
        DBMS_SQL.BIND_VARIABLE(curid, ':p2', 'YYY');
    END IF;
    IF (fieldc = 'Y') THEN
         DBMS_SQL.BIND_VARIABLE(curid, ':p3', 123); 
    END IF;

  ret := DBMS_SQL.EXECUTE(curId);  
  refCur := DBMS_SQL.TO_REFCURSOR(curId);
  
  -- DBMS_SQL.RETURN_RESULT(refCur);
  -- or 
  FETCH refCur BULK COLLECT INTO ...;
  -- process the result  
  CLOSE refCur;
  
END;
 

Но это может быть излишним для вашей ситуации.

Ответ №3:

Не используйте динамический SQL; просто используйте один оператор SQL:

 SELECT *
FROM   tablea
WHERE  id = 9999
AND    (  ( fielda = 'Y' AND a = 'XXX' )
       OR ( fieldb = 'Y' AND b = 'YYY' )
       OR ( fieldc = 'Y' AND c = 1234  )
       );
 

(Примечание: если вы выполняете запрос только из одной таблицы, то использование идентификатора таблицы в качестве префикса перед каждым столбцом является избыточным.)

(Примечание 2: если вы используете this is PL / SQL, тогда вам нужно будет использовать SELECT ... INTO ... FROM ... [или BULK COLLECT INTO ] .)


Вы можете использовать приведенный выше запрос с переменными fielda fieldb и fieldc с помощью курсора PL / SQL:

 DECLARE
  cur SYS_REFCURSOR;
  
  fielda CHAR(1) := 'Y';
  fieldb CHAR(1) := 'N';
  fieldc CHAR(1) := 'Y';
  
  data tablea%ROWTYPE;
BEGIN
  OPEN cur FOR
    SELECT *
    FROM   tablea
    WHERE  id = 9999
    AND    (  ( fielda = 'Y' AND a = 'XXX' )
           OR ( fieldb = 'Y' AND b = 'YYY' )
           OR ( fieldc = 'Y' AND c = 1234  )
           );

  LOOP
    FETCH cur INTO data;
    EXIT WHEN cur%NOTFOUND;
    DBMS_OUTPUT.PUT_LINE(
      data.a || ' ' || data.b || ' ' || data.c || ' ' || data.x
    );
  END LOOP;
END;
/
 

или, BULK COLLECT INTO коллекции:

 DECLARE
  fielda CHAR(1) := 'Y';
  fieldb CHAR(1) := 'N';
  fieldc CHAR(1) := 'Y';
  
  a_table SYS.ODCIVARCHAR2LIST;
  b_table SYS.ODCIVARCHAR2LIST;
  c_table SYS.ODCIVARCHAR2LIST;
  x_table SYS.ODCINUMBERLIST;
BEGIN
  SELECT a, c, b, x
  BULK COLLECT INTO a_table, b_table, c_table, x_table
  FROM   tablea
  WHERE  id = 9999
  AND    (  ( fielda = 'Y' AND a = 'XXX' )
         OR ( fieldb = 'Y' AND b = 'YYY' )
         OR ( fieldc = 'Y' AND c = 1234  )
         );

  FOR i IN 1 .. a_table.COUNT LOOP
    DBMS_OUTPUT.PUT_LINE(
      a_table(i) || ' ' || b_table(i) || ' ' || c_table(i) || ' ' || x_table(i)
    );
  END LOOP;
END;
/
 

Что для образца данных:

 CREATE TABLE tablea ( id, a, b, c, x ) AS
SELECT 9999, 'XXX', 'AAA', 1111, 1 FROM DUAL UNION ALL
SELECT 9999, 'BBB', 'YYY', 2222, 2 FROM DUAL UNION ALL
SELECT 9999, 'CCC', 'CCC', 1234, 3 FROM DUAL;
 

Оба вывода:

 XXX AAA 1111 1
CCC CCC 1234 3
 

db<>скрипка здесь

Комментарии:

1. Извините, я должен объяснить лучше

2. @SirLancillotto Ваше обновление ничего не меняет, вам по-прежнему нужен тот же запрос.