2014-12-31 3 views
1

Я хочу использовать отдельные столбцы в предложении WHERE на основе INPUT, полученного в хранимой процедуре.Oracle - изменение столбцов в выражении WHERE на основе ввода

  • Если TYPE_DEFINITION = 'SUP' затем использовать колонку ПОСТАВЩИК

  • Если TYPE_DEFINITION = 'CAT' затем использовать КАТ колонку

Я знаю, что могу написать два отдельных SELECT «S с помощью a CASE заявление, но это будет очень глупым и избыточным. Любой более чистый способ сделать это?

CREATE OR REPLACE PROCEDURE SG.STORED_PROCEDURE (
    TYPE_DEFINITION  IN VARCHAR2, 
    VALUE    IN VARCHAR2, 
    STORELIST   IN VARCHAR2) 
AS 
BEGIN 
     SELECT O.ORGNUMBER, 
      S.SKU, 
     FROM SKU S JOIN ORG O ON S.ORGID = O.ORGID 
     WHERE     
      AND O.ORGNUMBER IN (STORELIST) 
      AND (CASE TYPE_DEFINITION 
        WHEN 'SUP' THEN S.SUPPLIER = VALUE 
        ELSE S.CATEGORY = VALUE 
        END); 
END; 
/

ответ

1

В вашей процедуре необходимо использовать dynamic sql.

Что-то вроде этого:

CREATE OR REPLACE PROCEDURE SG.STORE_PROC (
    TYPE_DEFINITION  IN VARCHAR2, 
    VALUE    IN VARCHAR2, 
    STORELIST   IN VARCHAR2) 
AS 
    TYPE EmpCurTyp IS REF CURSOR; 
    v_emp_cursor EmpCurTyp; 
    v_stmt_str  VARCHAR2(200); 
    v_orgnumber  VARCHAR2(200); 
    v_sku   VARCHAR2(200); 
BEGIN 
    v_stmt_str := 'SELECT O.ORGNUMBER, S.SKU,FROM SKU S JOIN ORG O ON S.ORGID = O.ORGID '; 
    if type_definition = 'SUP' then 
    v_stmt_str := v_stmt_str || 'WHERE s.supplier = :v'; 
    else 
    v_stmt_str := v_stmt_str || 'WHERE s.category = :v'; 
    end if; 


    -- Open cursor & specify bind variable in USING clause: 
    OPEN v_emp_cursor FOR v_stmt_str USING value; 

    -- Fetch rows from result set one at a time: 
    LOOP 
    FETCH v_emp_cursor INTO v_orgnumber, v_sku; 
    -- you can do something here with your values 
    EXIT WHEN v_emp_cursor%NOTFOUND; 
    END LOOP; 

    -- Close cursor: 
    CLOSE v_emp_cursor; 
END; 
/
1

Ваш код находится очень близко. CASETHEN должен возвращать выражение, а не условие. Но CASE может использоваться как часть состояния, просто переместите = VALUE наружу.

Изменить это:

  AND (CASE TYPE_DEFINITION 
       WHEN 'SUP' THEN S.SUPPLIER = VALUE 
       ELSE S.CATEGORY = VALUE 
       END); 

Для этого:

  AND VALUE = (CASE TYPE_DEFINITION 
         WHEN 'SUP' THEN S.SUPPLIER 
         ELSE S.CATEGORY 
         END); 

Ваш код имеет смысл. Это ограничение, вероятно, является результатом того, что Oracle не полностью поддерживает Booleans.


UPDATE

Если вы столкнулись с проблемами производительности вы можете использовать динамический SQL или убедитесь, что статический SQL правильно с помощью FILTER операций. Когда Oracle строит план выполнения, он может использовать переменные связывания, такие как константы, и выбирать другой план на основе ввода. Как Бен отметил, эти FILTER операции не всегда работают отлично, иногда это может помочь, если вы использовать упрощенные условия, как это:

(TYPE_DEFINITION = 'SUP' AND S.SUPPLIER = VALUE) 
OR 
((TYPE_DEFINITION <> 'SUP' OR TYPE_DEFINITION IS NULL) AND S.CATEGORY = VALUE) 
+0

Вы можете сделать это, и для одного состояния, было бы хорошо, но для что-то умеренно сложное (т. е. если есть более чем несколько условных частей логики), тогда вы можете получить некоторые фанковые планы. Моя рекомендация будет заключаться в том, что для чего-либо более трех или четырех динамических условий вы используете динамический SQL. Таким образом, вы получаете индивидуально настраиваемые планы запросов, и оптимизатор имеет больше шансов предсказать хорошие стратегии. – Ben

+1

@Ben Спасибо, это хороший момент. Я добавил некоторую информацию о работе с плохими планами со статическим SQL и несколькими условиями. –