2015-04-24 2 views
1

Я нахожусь в ситуации, когда моему коду нужно выполнить динамический оператор, сформированный неизвестным числом (по крайней мере, одним, но может быть больше) других динамических операторов, конкатенированных 'пересечением 'и' union '.Динамическое связывание для динамического запроса в Native Dynamic SQL

Вот пример с тремя querys (я знаю, что это может быть решена с одним запросом, я пытаюсь сохранить его простым):

sql1 varchar2(500) := 'select empno from emp where deptno = :1'; 
sql2 varchar2(500) := 'select empno from emp where sal > :2 and hiredate >=:3'; 
sql3 varchar2(500) := 'select empno from emp where sal <= :2 and hiredate =:3' 
realStatement varchar(1500) := sql1 || ' insersect ' || sql2 || ' union ' sql3; 

Теперь, учитывая, что число вспомогательных утверждений это неизвестно во время выполнения, но известны все значения переменных связывания (т.е. deptno, sal и hiredate всегда будут: 1,: 2 и: 3 соответственно). Я не могу использовать «EXECUTE IMMEDIATE realStatement USING» формы, потому что это привязка позиционные и для этого примера, я должен передать Сала и HireDate параметров дважды, в результате чего в заявлении:

EXECUTE IMMEDIATE realStatement USING l_deptno,l_sal,l_hiredate,l_sal,l_hiredate; 

Что нет никакого способа Я мог заранее знать, чтобы включать все повторения для каждого подзаголовка.

Я знаю, что я мог бы использовать пакет DBMS_SQL с функцией bind(), но производительность в 1,5-3 раза хуже, чем с родной динамикой (из oracle docs), и в этом случае производительность имеет значение.

То, что я на самом деле делаю, заменяет все «: 1» вхождения с помощью l_deptno, все «: 2» ​​вхождения с l_sal и все ': 3' вхождения с 'to_date (' '' || l_hiredate || «» «„“DD/MM/YYYY„“)» в строке realStatement перед его выполнением, как это:

realStatement := replace(realStatement,':1',l_deptno); 
realStatement := replace(realStatement,':2',l_sal); 
realStatement := replace(realStatement,':3','to_date(''' || l_hiredate || ''',''DD/MM/YYYY'')'); 
EXECUTE IMMEDIATE realStatement; 

Но я не уверен, что это лучшее решение, вопросы:

  1. есть способ улучшить производительность или передать привязки динамически с использованием встроенной динамической S QL?

  2. будет использовать результат DBSM_SQL с более высокой производительностью , чем у выбранного решения?

+0

Будет ли 'sql1 varchar2 (500): = 'выбрать empno из emp где deptno =' || : 1 удовлетворяют ваши потребности? –

+0

@ Politank-Z Я забыл упомянуть, что строка запроса восстановлена ​​из таблицы, поэтому ссылка уже существует, плюс она не обязательно будет всегда в конце запроса. – Typo

+0

Где вы краснели о ухудшении производительности для привязки()? –

ответ

1

переписать свои заявления:

sql1 varchar2(500) := 'select empno from emp where deptno = :1'; 
sql2 varchar2(500) := 'select empno from emp where sal > :2 and hiredate >=:3'; 
sql3 varchar2(500) := 'select empno from emp where sal <= :2 and hiredate =:3' 
realStatement varchar(1500) := sql1 || ' insersect ' || sql2 || ' union ' sql3; 

использовать WITH положение следующим образом:

sql0 varchar2(500) := 'WITH par AS (SELECT :1 AS P1, :2 AS P2, :3 AS P3 FROM dual)'; 
sql1 varchar2(500) := '(select empno from emp join par where deptno = par.p1)'; 
sql2 varchar2(500) := '(select empno from emp join par where sal > par.p2 and hiredate >=par.p3)'; 
sql3 varchar2(500) := '(select empno from emp join par where sal <= par.p2 and hiredate = par.p3)'; 
realStatement varchar(2000) := sql0 || ', sql1 as ' || sql1 || ', sql2 as ' || sql2 || ', sql3 as ' || sql3 || ' select * from sql1 intersect select * from sql2 union select * from sql3'; 

или (при отсутствии повторного использования подзапросов):

sql0 varchar2(500) := 'WITH par AS (SELECT :1 AS P1, :2 AS P2, :3 AS P3 FROM dual)'; 
sql1 varchar2(500) := 'select empno from emp join par where deptno = par.p1'; 
sql2 varchar2(500) := 'select empno from emp join par where sal > par.p2 and hiredate >=par.p3'; 
sql3 varchar2(500) := 'select empno from emp join par where sal <= par.p2 and hiredate = par.p3'; 
realStatement varchar(2000) := sql0 || ' ' || sql1 || ' intersect ' || sql2 || ' union ' || sql3; 

затем выполнить его, используя 3 связывающие переменные: EXECUTE IMMEDIATE realStatement USING l_deptno,l_sal,l_hiredate

+0

Привет, Павел, спасибо, что ответили. Я попробую и вернусь к вам, но выглядит многообещающе. – Typo

+0

Привет, Павел, я пробовал ваше предложение и частично работал, что мне пришлось изменить, так это то, как значения из предложения to ссылаются в подзапросах. par.p1 "ссылка не будет разрешена, она должна быть указана как" select p1 from par ". Какой (из того, что я прочитал), так это то, как предложение WITH работает так или иначе, если вы исправите это в своем ответе, я приму его как правильно. – Typo

+0

@ Typo извините, у меня не было времени попробовать, я добавил отсутствующий 'join par' для каждого подзапроса (вы можете заменить' join par where' на 'join par on' тоже, я тоже не хотел бы менять инструкции много) –

0

Как уже упоминалось, Oracle документации (http://docs.oracle.com/cd/B19306_01/appdev.102/b14251/adfns_dynamic_sql.htm#BJEBACEH) говорит:

Программы, которые используют родной динамический SQL гораздо быстрее, чем программы, которые используют пакет DBMS_SQL. Как правило, собственные динамические операторы SQL выполняют в 1,5-3 раза лучше, чем эквивалентные вызовы DBMS_SQL.

Но с другой стороны - вы можете увидеть некоторые Comparision здесь: http://www.toadworld.com/products/toad-for-oracle/w/toad_for_oracle_wiki/231.dbms-sql-vs-execute-immediate.aspx:

Есть много преимуществ использования DBMS_SQL над EXECUTE IMMEDIATE:

  • легче писать и тип (для меня в любом случае - я не могу писать IMMEDAITE IMMEADIATE IMMEDIATE

  • Меньше защелкивания с помощью DBMS_SQL.

  • Меньше парсов с DBMS_SQL.

  • Лучшее масштабирование вашего приложения с помощью DBMS_SQL из-за вышеизложенного.

Недостатки:

  • Больше набрав в целом с DBMS_SQL.

Я хотел бы сказать - если у вас есть много таких звонков, то связывание переменных и уменьшение количества синтаксического анализа даст значительное влияние. Но - если у вас не много вызовов, а один запрос возвращает много строк - тогда EXECUTE IMMEDIATE без переменных привязки (ваше решение с заменой) может быть быстрее.

В любом случае - если производительность релевантна - сделайте оба решения (с EXECUTE IMMEDIATE и DBMS_SQL) и сравните результаты.

0

Когда я учился несколько лет назад, разница в производительности была темой (это был Oracle 9i). Мы смогли воспроизвести разницу. Однако, когда я делаю те же тесты сегодня (с Oracle 11), я больше не получаю никаких различий в производительности. Используйте способ, который более удобен для вас.

+0

, какие тесты вы выполнили? – Typo

+0

Я выполняю тот же запрос, что и с 'EXECUTE IMMEDIATE' и с' DBMS_SQL' несколько раз -> нет разницы. 10 лет назад с Oracle 9i разница была значительной. –