Эта статья посвящена двум особенностям работы PL/SQL-машины, которая в версиях 9.x объединена с SQL-машиной. Интересные особенности, которые могут всплыть при переносе программного обеспечения на новую версию сервера Oracle... По мотивам ответа Тома Кайта на вопросы, заданные 15 июня 2003 года.
USING - ключевое слово PL/SQL!
Том,
Я хотел бы задать два вопроса, которые меня сильно сбивают с толку.
Первый вопрос: Один из разработчиков написал следующий код в Oracle 8.1.7.3:
create table auxtab as select from dual;
create or replace procedure p_insert_auxtab as
begin
insert into auxtab using (select * from dual);
commit;
end;
/
Procedure created.
SQL> show errors
No errors.
При попытке выполнения тех же действий в версии 9.2.0.3.0 получаем сообщение об ошибке:
create table auxtab as select from dual;
create or replace procedure p_insert_auxtab as
begin
insert into auxtab using (select * from dual);
commit;
end;
/
Warning: Procedure created with compilation errors.
SQL> show errors
Errors for PROCEDURE P_INSERT_AUXTAB:
LINE/COL ERROR
-------- ----------------------------------------------------
3/1 PL/SQL: SQL Statement ignored
3/31 PL/SQL: ORA-00926: missing VALUES keyword
Но если сделать так:
SQL> create or replace procedure p_insert_auxtab as
2 begin
3 insert into auxtab using select * from dual;
4 commit;
5* end;
SQL> /
Procedure created.
SQL> show errors
No errors.
Я никогда не думал, что слово "using" может использоваться так. Я использовал слово using в операторах типа execute immediate или при открытии курсора. Мой вопрос: правильно ли использовать слово using в операторе "insert into..."? Почему разработчик смог скомпилировать и выполнить процедуру в версии 8i со скобками, а в версии 9i это уже не прошло?
Второй вопрос: Один из разработчиков сделал следующее в версии 8.1.7.3
SQL> create table test2
2 (colnum number)
3 ;
Table created.
SQL> create or replace procedure p_test2 as
2 x number:=0;
3 begin
4 select colnum into x from test2 where colnum mod 4 =0;
5 dbms_output.put_line(to_char(x));
6* end;
SQL> /
Procedure created.
SQL> show error
No errors.
В версии 9i я получаю:
SQL> create table test2
2 (colnum number)
3 ;
Table created.
SQL> create or replace procedure p_test2 as
2 x number:=0;
3 begin
4 select colnum into x from test2 where colnum mod 4 =0;
5 dbms_output.put_line(to_char(x));
6* end;
7 /
Warning: Procedure created with compilation errors.
SQL> show errors
Errors for PROCEDURE P_TEST2:
LINE/COL ERROR
-------- -----------------------------------------------------------------
4/1 PL/SQL: SQL Statement ignored
4/59 PL/SQL: ORA-00920: invalid relational operator
Почему версия 8i позволяла использовать функцию mod как оператор? Не должна ли былап выдаваться ошибка и в версии 8i? Я просмотрел документацию Oracle и не нашел ни одного примера, где mod используется как оператор. Можно ли использовать mod как оператор? Почему процедура перестала успешно компилироваться в версии 9i?
Ответ Тома Кайта
Первый вопрос:
insert into T
(select * from dual);
Такой оператор вас не удивляет, перавда? Так что:
insert into T T1
(select * from dual);
тоже не удивит. А дальше:
insert into T "using"
(select * from dual);
Это ведь аналогичная конструкция, как и:
insert into T using
(select * from dual);
USING - всего лишь "корреляционное имя", ПСЕВДОНИМ таблицы T, а не ключевое слово. Но в версии 9i появилось ключевое слово USING в языке PL/SQL, что усложнило ситуацию:
ops$tkyte@ORA920> create table t as select * from dual where 1=0;
Table created.
ops$tkyte@ORA920> begin
2 insert into t using (select * from dual);
3 end;
4 /
end;
*
ERROR at line 3:
ORA-06550: line 2, column 16:
PL/SQL: ORA-00926: missing VALUES keyword
ORA-06550: line 2, column 2:
PL/SQL: SQL Statement ignored
ops$tkyte@ORA920> begin
2 insert into t using select * from dual;
3 end;
4 /
PL/SQL procedure successfully completed.
ops$tkyte@ORA920> begin
2 insert into t "using" (select * from dual);
3 end;
4 /
PL/SQL procedure successfully completed.
ops$tkyte@ORA920> begin
2 insert into t "using" select * from dual;
3 end;
4 /
PL/SQL procedure successfully completed.
Но если коротоко -- USING интерпретировалось как псевдоним, а не как ключевое слово. Его просто не нужно указывать.
Второй вопрос:
Уникальная ситуация - я с такой не сталкивался. Проблема связана с тем, что в версиях до 8i включительно PL/SQL-машина использовала отдельный анализатор SQL. В PL/SQL все операторы типа =, < и т.п. определялись как "функции", они переписывались. Если включить sql_trace и посмотреть на SQL, сгенерированный из PL/SQL, можно увидеть следующее:
SELECT COLNUM
FROM
T WHERE MOD (COLNUM,4) = 0
Хотя в исходном коде и было написано:
ops$tkyte@ORA817DEV> create table t ( colnum number );
Table created.
ops$tkyte@ORA817DEV> insert into t select rownum-1 from all_users where rownum <=4;
4 rows created.
ops$tkyte@ORA817DEV> create or replace procedure p_test2 as
2 x number:=0;
3 begin
4 select colnum into x from t where colnum mod 4 =0;
5 dbms_output.put_line(to_char(x));
6 end;
7 /
Procedure created.
ops$tkyte@ORA817DEV> exec p_test2;
0
PL/SQL procedure successfully completed.
ops$tkyte@ORA817DEV> select colnum from t where colnum mod 4 = 0;
select colnum from t where colnum mod 4 = 0
*
ERROR at line 1:
ORA-00920: invalid relational operator
Это нигде не описано в документации. Работало в 8i "случайно". Вот такие "странные, но интересные" особенности реализации...
Источник
Следующая > |
---|