Динамическая вставка - ORA-00923: ключевое слово FROM не найдено там, где ожидается - PullRequest
1 голос
/ 01 апреля 2019

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

  CREATE TABLE TEST_DATA_QTY_AND
  (

  TABLE_ID VARCHAR2(30),
  FLD_ID VARCHAR2(30),
  MEASURE NUMBER(1, 2),
  DATA_T DATE DEFAULT SYSDATE,
  NOTES VARCHAR2(255)

  );


CREATE OR REPLACE PROCEDURE DATA_QTY_AND(pTable IN VARCHAR2, pField IN VARCHAR2)

 IS


v_sql varchar2(2000);


v_sql := 'INSERT INTO TEST_DATA_QTY_AND (TABLE_ID, FLD_ID, MEASURE)'||
         'VALUES('||
         pTable||', '||pField||', ('||
         'SELECT SUM(CASE WHEN '||pField||' <> 0 THEN 1 END) /COUNT(*) FROM'||
         pTable||'));';

EXECUTE IMMEDIATE(v_sql);

COMMIT;

   EXCEPTION
   ...
   END;

Однако я продолжаю получать сообщение об ошибке00923: ключевое слово FROM не найдено там, где ожидается.Любая помощь приветствуется.

Спасибо

Ответы [ 4 ]

1 голос
/ 01 апреля 2019

Ваше объяснение не очень понятно, я пытаюсь уточнить, что я понял (растягивая свое воображение), следующим примером / дальнейшим описанием

Допустим, у вас есть две основные таблицы

create table tbl_1(
    id          int not null primary_key,
    value_1     int 
);
create table tbl_2(
    id          int not null primary_key,
    value_2     int 
);

Если вы передадите tbl_1, value_1 в качестве параметров вашей процедуры, ваш SQL вставки должен быть

INSERT INTO TEST_DATA_QTY_AND (TABLE_ID, FLD_ID, MEASURE) values ('tbl_1', 'value_1', (select sum(case when value_1 <> 0 then 1 else 0 end) from tbl_1))

Когда вы передаете tbl_2, value_2 в качестве параметров вашей процедуре, ваш SQL вставки должен быть

INSERT INTO TEST_DATA_QTY_AND (TABLE_ID, FLD_ID, MEASURE) values ('tbl_2', 'value_2', (select sum(case when value_2 <> 0 then 1 else 0 end) from tbl_2))

Если это понимание верно, то должно работать следующее

CREATE OR REPLACE PROCEDURE DATA_QTY_AND(pTable IN VARCHAR2, pField IN VARCHAR2)
IS
  v_sql varchar2(2000);
BEGIN

  v_sql :=  utl_lms.format_message(
        'INSERT INTO TEST_DATA_QTY_AND (TABLE_ID, FLD_ID, MEASURE) VALUES('''%s''', '''%s''', '
      ||'(SELECT SUM(CASE WHEN %s <> 0 THEN 1 ELSE 0 END)/ COUNT(*) FROM %s))',  
      pTable, pField, pField, pTable);  

EXECUTE IMMEDIATE(v_sql);
END;

Примечание Я оставил вам обработку транзакций и исключений в качестве упражнения.Также обратите внимание, что на момент написания этой статьи у меня не было доступа к базе данных Oracle, поэтому я не мог создавать / компилировать, чтобы отсеять любую ошибку / проблему.Вы должны попытаться решить проблемы (если таковые имеются).Если вы не можете оставить комментарий, я сделаю это, когда у меня будет доступ к экземпляру БД.

ОДНАКО, все это только тогда, когда мое решение вашей проблемы верно.

1 голос
/ 01 апреля 2019

ORA-00923: ключевое слово FROM не найдено там, где ожидается.

Ваша строка выглядит следующим образом:

FROM'||
pTable||'));';

Нет пробела после литерала FROM таким образом, собранный код объединит параметр имени таблицы с FROM, чтобы сделать строку типа:

SEELCT COLUMN_1 FROMTABLE_23

Отсюда и ошибка.

Динамический SQL сложен, потому что он превращает ошибки компиляции во время выполненияошибки.Вы избавите себя от большого горя, если добавите отладку.Простой dbms_output.put_line(v_sql); позволил бы вам увидеть собранный код: вы, вероятно, сразу заметили бы свой шаровар.

1 голос
/ 01 апреля 2019

Вам необходимо поместить одинарные кавычки вокруг буквенных строковых значений, и одинарные кавычки, используемые для хранения динамической строки SQL, не учитываются. Мы можем попытаться использовать две одинарные кавычки для представления одной буквальной одинарной кавычки:

v_sql := 'INSERT INTO TEST_DATA_QTY_AND (TABLE_ID, FLD_ID, MEASURE) ' ||
         'VALUES(' ||
         '''' || pTable || ''', ''' || pField || ''', (' ||
         'SELECT SUM(CASE WHEN ' || pField || ' <> 0 THEN 1 END) / COUNT(*) FROM ' ||
         pTable || '))';

Обратите внимание, что в подзапросе вы сравниваете pField, текстовую переменную, с 0, без кавычек, то есть целое число. Это не имеет смысла, поэтому в приведенном выше фрагменте я сравниваю с '0' вместо числа.

0 голосов
/ 01 апреля 2019

Вы можете использовать execute immediate с переменными после using ключевое слово

create or replace procedure data_qty_and ( pTable varchar2, pField varchar2 ) is
 v_sql varchar2(2000);
begin
  v_sql := 'insert into test_data_qty_and( table_id, fld_id, measure  )
            select :1, :2, sum(case when :2 <> 0 then 1 end ) /count(*) from '||pTable;
  dbms_output.put_line(v_sql);
  execute immediate v_sql using pTable, pField, pField;

  commit;
 exception when others then dbms_output.put_line(sqlerrm);
end;

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

...