Компиляция PL / SQL при условии наличия объекта базы данных - PullRequest
5 голосов
/ 12 октября 2011

Возможна ли условная компиляция в Oracle, где условием является наличие объекта базы данных (в частности, таблицы или представления или синонима)?Я хотел бы иметь возможность сделать что-то вроде этого:

sp_some_procedure is
    $IF /*check if A exists.*/ then 
        /* read from and write to A as well as other A-related non-DML stuff...*/
    $ELSE /*A doesn't exist yet, so avoid compiler errors*/
        dbms_output.put_line('Reminder: ask DBA to create A!')
    $ENDIF
end;

Ответы [ 5 ]

4 голосов
/ 04 декабря 2011

Да, это так.Вот пример, где первая хранимая процедура хочет выбрать из XALL_TABLES, но если этой таблицы не существует, выберите из двойной.Наконец, поскольку у меня нет объекта XALL_TABLES, первая хранимая процедура выбирает из двойного.Второй, делает то же самое с объектом ALL_TABLES.Поскольку ALL_TABLES существует, вторая хранимая процедура выбирает из all_tables, но не из DUAL.

Этот тип конструкции полезен, когда пакет должен быть развернут во всей вашей базе данных и использовать таблицы, которые развернуты не везде .... (хорошо, возможно, есть концептуальная проблема, но это происходит).

--conditionals compilation instructions accept only static condition (just with constants)
--passing sql bind variable doesn't work 
--To pass a value to a conditional compilation instruction, I bypasses the use of input parameters of the script
--these 4 next lines affect a value to the first and the second input parameter of the script
--If your originally script use input script parameter, use the next free parameter ...
column param_1 new_value 1 noprint
select nvl(max(1), 0) param_1 from all_views where owner = 'SYS' and view_name = 'XALL_TABLES';
column param_2 new_value 2 noprint
select nvl(max(1), 0) param_2 from all_views where owner = 'SYS' and view_name = 'ALL_TABLES';

CREATE or replace PACKAGE my_pkg AS
  function test_xall_tables return varchar2;
  function test_all_tables return varchar2;
END my_pkg;
/

CREATE or replace PACKAGE BODY my_pkg AS

  function test_xall_tables return varchar2 is
    vch varchar2(50);
  begin
    $IF (&1 = 0) $THEN
      select 'VIEW XALL_TABLES D''ONT EXISTS' into vch from dual;
    $ELSE
      select max('VIEW XALL_TABLES EXISTS') into vch from XALL_TABLES;
    $END        
    return vch;      
  end test_xall_tables;

  function test_all_tables return varchar2 is
    vch varchar2(50);
  begin
    $IF (&2 = 0) $THEN
      select 'VIEW ALL_TABLES D''ONT EXISTS' into vch from dual;
    $ELSE
      select max('VIEW ALL_TABLES EXISTS') into vch from ALL_TABLES;
    $END
    return vch;
  end test_all_tables;             
END my_pkg;
/

тест:

select my_pkg.test_xall_tables from dual;

дают

ПОСМОТРЕТЬ XALL_TABLES D'ONT EXISTS

select my_pkg.test_all_tables from dual;

дай

ПОСМОТРЕТЬ ВСЕ_ ТАБЛИЦЫ СУЩЕСТВУЕТ

2 голосов
/ 13 октября 2011

Использование динамического SQL для создания констант пакета для отслеживания существующих объектов, а затем использование этих констант в условной компиляции.

--E.g., say there are two possible tables, but only one of them exists.
--create table table1(a number);
create table table2(a number);


--Create a package with boolean constants to track the objects.
--(Another way to do this is to use ALTER SESSION SET PLSQL_CCFLAGS)
declare
  table1_exists_string varchar2(10) := 'true';
  table2_exists_string varchar2(10) := 'true';
  temp number;
begin
  begin
    execute immediate 'select max(1) from table1 where rownum <= 1' into temp;
  exception when others then
    table1_exists_string := 'false';
  end;

  begin
    execute immediate 'select max(1) from table2 where rownum <= 1' into temp;
  exception when others then
    table2_exists_string := 'false';
  end;

  execute immediate '
    create or replace package objects is
      table1_exists constant boolean := '||table1_exists_string||';
      table2_exists constant boolean := '||table2_exists_string||';
    end;
  ';
end;
/

--Look at the results in the source:
select * from user_source where name = 'OBJECTS';


--Create the object that refers to the tables.
create or replace function compile_test return varchar2 is
    v_test number;
begin
    $if objects.table1_exists $then
        select max(1) into v_test from table1;
        return 'table1 exists';
    $elsif objects.table2_exists $then
        select max(1) into v_test from table2;
        return 'table 2 exists';
    $else
    return 'neither table exists';
    $end
end;
/

--Check the dependencies - only TABLE2 is dependent.
select * from user_dependencies where name = 'COMPILE_TEST';

--Returns 'table 2 exists'.
select compile_test from dual;

Смешивание динамического SQL, динамического PL / SQL и условной компиляции обычно оченьзлая идея.Но это позволит вам поместить весь ваш уродливый динамический SQL в один установочный пакет и поддерживать реальное отслеживание зависимостей.

Это может хорошо работать в полудинамической среде;например, программа, которая устанавливается с различными наборами объектов, но не часто переключается между ними.

(Кроме того, если весь смысл этого заключается просто в замене страшных сообщений об ошибках дружественными предупреждениями, по моему мнению,Это очень плохая идея. Если ваша система выходит из строя, сбой должен быть очевидным, чтобы его можно было немедленно исправить. Большинство людей игнорируют все, что начинается с «Напоминание ...».)

2 голосов
/ 12 октября 2011

Я бы использовал 'EXECUTE IMMEDIATE' и предложение EXCEPTION.

1 голос
/ 12 октября 2011

Нет - это невозможно ... но если вы создадите хранимую процедуру, ссылающуюся на несуществующий объект БД, и попытаетесь скомпилировать ее, компиляция покажет ошибки ... хранимая процедура будет там, но "недействительная".... и ошибки компиляции доступны для администратора базы данных всякий раз, когда он смотрит на него ... поэтому я просто продолжил бы и создал все необходимые хранимые процедуры, если возникнут какие-либо ошибки компиляции, обратитесь к администратору базы данных (иногда объект существует, но хранимая процедура требуетразрешения на доступ к нему ...) ... после устранения причины ошибки можно просто перекомпилировать хранимую процедуру (через ALTER PROCEDURE MySchema.MyProcName COMPILE;), и все в порядке ...

Если вы не хотите, чтобы код был там, вы можете просто DROP выполнить процедуру и / или заменить через CREATE OR REPLACE ... с dbms_output.put_line('Reminder: ask DBA to create A!') в теле.

единственная альтернатива, как указывает Кевин EXECUTE IMMEDIATE при правильной обработке EXCEPTION ...

0 голосов
/ 13 октября 2011

Что я хотел бы сделать, это проверить существование через all_objects, что-то вроде:

declare

l_check_sql varchar2(4000);
l_cnt number;

begin

l_check_sql := q'{
select count(1)
from all_objects
where object_name = 'MY_OBJ'
and owner = 'MY_OWNER'
}';

execute immediate l_check_sql into l_cnt;

if (l_cnt > 0) then
  -- do something referring to MY_OBJ
else
  -- don't refer to MY_OBJ
end if;

end;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...