PL / SQL: ORA-00942, сообщение об ошибке в логически недоступном блоке else. Необходимо добавить НЕМЕДЛЕННОЕ ВЫПОЛНЕНИЕ, чтобы он заработал. Почему?

#sql #oracle #plsql

#sql #Oracle #plsql

Вопрос:

Я пытаюсь написать скрипт PL / SQL, который выполняет несколько инструкций SQL, если определенная таблица существует / не существует.

Например:

 SET SERVEROUTPUT ON;
DECLARE
  cnt NUMBER := 0;
  cnt_2 NUMBER := 0;
BEGIN
  SELECT count(*) INTO cnt FROM all_tables where TABLE_NAME='DOES_NOT_EXIST';
  IF cnt = 0 THEN
    dbms_output.put_line('Table does not exist');
  ELSE
    dbms_output.put_line('Table exists ' || cnt);
    SELECT COUNT(*) INTO cnt_2 from DOES_NOT_EXIST;
  END IF;
END;
/
  

Когда я выполняю это, я получаю сообщение об ошибке ниже

     SELECT COUNT(*) INTO cnt_2 from DOES_NOT_EXIST;
                                    *
ERROR at line 10:
ORA-06550: line 10, column 37:
PL/SQL: ORA-00942: table or view does not exist
ORA-06550: line 10, column 5:
PL/SQL: SQL Statement ignored
  

Но, если я добавлю EXECUTE IMMEDIATE в строку 11. Тогда все работает нормально.

 SET SERVEROUTPUT ON;
DECLARE
  cnt NUMBER := 0;
  cnt_2 NUMBER := 0;
BEGIN
  SELECT count(*) INTO cnt FROM all_tables where TABLE_NAME='DOES_NOT_EXIST';
  IF cnt = 0 THEN
    dbms_output.put_line('Table does not exist');
  ELSE
    dbms_output.put_line('Table exists ' || cnt);
    EXECUTE IMMEDIATE 'SELECT COUNT(*) INTO cnt_2 from DOES_NOT_EXIST';
  END IF;
END;
/

  

Теперь это работает

 Table does not exist

PL/SQL procedure successfully completed.
  
  1. Не могли бы вы, пожалуйста, помочь мне понять, почему сообщается об ошибке, если ELSE блок не может быть достигнут логически? cnt Переменная всегда будет 0 , тогда я бы предположил, что ELSE блок никогда не будет достигнут.

  2. Почему добавление EXECUTE IMMEDIATE не приводит к ошибке?

  3. Означает ли это, что я должен добавить EXECUTE IMMEDIATE для всех таких операторов в ELSE блоке?

Я искал в SO и других местах, но не смог найти ответ. Честно говоря, я не знаю, какие другие условия поиска использовать. Все поисковые запросы приводят к общим ошибкам. Итак, я задаю этот вопрос. Если на этот вопрос уже дан ответ, пожалуйста, укажите мне на него и закройте это.

Спасибо.

Ответ №1:

Чего вам не хватает, так это того, что инструкции обрабатываются в два этапа; они компилируются перед их выполнением.

На этапе компиляции идентификаторы (таблицы, столбцы и многое другое) идентифицируются и просматриваются. Если имя таблицы (или имя столбца или что-то еще) не найдено, вы получаете ошибку компиляции.

Использование execute immediate замыкает этот процесс. Вместо «немедленного выполнения» это действительно «задержка компиляции». Инструкция компилируется и выполняется во время выполнения. Вот почему execute immediate предотвращает ошибку.

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

1. Спасибо, Гордон. Есть ли лучший способ закодировать такую логику? или я застрял с добавлением EXECUTE IMMEDIATE в блоке ELSE для всех операторов w.r.t в эту таблицу?

2. @SantoshSrinivas . , , Я не думаю, что есть альтернатива, если вы хотите ссылаться на несуществующие таблицы.

Ответ №2:

Похоже, вам нужно что-то вроде этого:

 select
  owner,
  table_name,
  xmlcast(
    xmlquery(
      '/ROWSET/ROW/CNT' 
      passing xmltype(dbms_xmlgen.getXML('select count(*) cnt from "'||owner||'"."'||table_name||'"'))
      returning content null on empty
      )
      as int
   ) as cnt
from all_tables
where owner in ('XTENDER',user);
  

Этот запрос возвращает количество строк для каждой таблицы из all_tables, отфильтрованных вашими предикатами