Oracle Dynamic PL / SQL из таблицы - PullRequest
       16

Oracle Dynamic PL / SQL из таблицы

0 голосов
/ 21 декабря 2018

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

DECLARE
  in_table_name VARCHAR2(30) := 'TEST_TABLE';

  l_table_record VARCHAR2(30) := in_table_name || '_r';
  l_table_table VARCHAR2(30) := in_table_name || '_t';
  l_table_list VARCHAR2(30) := in_table_name || '_l';

  TYPE validation_cols_r IS RECORD (COLUMN_NAME COLUMNS_TO_VALIDATE.COLUMN_NAME%TYPE,
                                    VALIDATION_TYPE COLUMNS_TO_VALIDATE.VALIDATION_TYPE%TYPE,
                                    CUSTOM_SQL COLUMNS_TO_VALIDATE.CUSTOM_SQL%TYPE
                                   );
  TYPE validation_cols_t IS TABLE OF validation_cols_r;
  l_validation_columns validation_cols_t;

  l_first NUMBER := 0; -- Simple boolean flag, always set when using, assume value changes if leaving current block
  l_build_select VARCHAR2(4000) := 'SELECT';
  l_build_record VARCHAR(4000) := 'TYPE ' || l_table_record || ' IS RECORD (';
  l_build_table VARCHAR2(4000) := 'TYPE ' || l_table_table || ' IS TABLE OF ' || l_table_record;
  l_build_list VARCHAR2(4000) := l_table_list || ' ' || l_table_table;

  l_build_main VARCHAR2(4000);
BEGIN

  SELECT COLUMN_NAME, VALIDATION_TYPE, CUSTOM_SQL
  BULK COLLECT INTO l_validation_columns
  FROM COLUMNS_TO_VALIDATE
  WHERE TABLE_NAME = in_table_name
  ;

  -- Generate the SELECT statement to get all the records
  l_first := 1;
  FOR indx IN 1 .. l_validation_columns.COUNT
  LOOP
    IF (l_first = 1) THEN
      l_build_select := l_build_select || ' ' || l_validation_columns(indx).COLUMN_NAME;
      l_build_record := l_build_record || l_validation_columns(indx).COLUMN_NAME || ' ' || in_table_name || '.' || l_validation_columns(indx).COLUMN_NAME || '%TYPE';
      l_first := 0;
    ELSE
      l_build_select := l_build_select || ', ' || l_validation_columns(indx).COLUMN_NAME;
      l_build_record := l_build_record || ', ' || l_validation_columns(indx).COLUMN_NAME || ' ' || in_table_name || '.' || l_validation_columns(indx).COLUMN_NAME || '%TYPE';
    END IF;
  END LOOP;
  l_build_select := l_build_select || ' BULK COLLECT INTO ' || l_table_list || ' FROM ' || in_table_name;
  l_build_record := l_build_record || ')';

  FOR vt IN 1 .. l_validation_columns.COUNT
  LOOP
    l_build_main :=
    '
      DECLARE
      ' || l_build_record || ';
      ' || l_build_table || ';
      ' || l_build_list || ';
      BEGIN
        ' || l_build_select || ';

        DBMS_OUTPUT.PUT_LINE(''Count: '' || ' || l_table_list || '.COUNT);
        FOR rec IN 1 .. ' || l_table_list || '.COUNT
        LOOP
          DBMS_OUTPUT.PUT_LINE(''' || l_validation_columns(vt).COLUMN_NAME || ': '' || ' || l_table_list || '(rec).' || l_validation_columns(vt).COLUMN_NAME || ');
          CASE ''' || l_validation_columns(vt).VALIDATION_TYPE || '''
            WHEN ''RANGE'' THEN
              IF (' || l_table_list || '(rec).' || l_validation_columns(vt).COLUMN_NAME || ' NOT BETWEEN ' || l_validation_columns(vt).CUSTOM_SQL || ')
              THEN
                DBMS_OUTPUT.PUT_LINE(''Fails range validation'');
              END IF;
            ELSE
              DBMS_OUTPUT.PUT_LINE(''No type of validation'');
          END CASE;

        END LOOP;
      END;
    ';
    EXECUTE IMMEDIATE l_build_main;
--    DBMS_OUTPUT.PUT_LINE(l_build_main);
  END LOOP;
END;

Это приводит к ошибке:

Отчет об ошибке - ORA-06550: строка 16, столбец 47: PLS-00103: Обнаружен символ «)» при ожидании одного из следующих событий: в like2 like4 like4 между членами подмножества ORA-06550: строка 26, столбец 4: PLS-00103: обнаружен символ «конец файла»при ожидании одного из следующего:;Символ ";"был заменен на «конец файла» для продолжения.ORA-06512: в строке 81 06550. 00000 - «строка% s, столбец% s: \ n% s» * Причина: обычно ошибка компиляции PL / SQL.* Действие:

Когда я вывожу динамический sql с помощью DBMS_OUTPUT, а затем запускаю его вручную, он работает правильно.

DECLARE
  TYPE TEST_TABLE_r IS RECORD (EMAIL TEST_TABLE.EMAIL%TYPE, GENDER TEST_TABLE.GENDER%TYPE, NAME TEST_TABLE.NAME%TYPE, PID TEST_TABLE.PID%TYPE);
  TYPE TEST_TABLE_t IS TABLE OF TEST_TABLE_r;
  TEST_TABLE_l TEST_TABLE_t;
BEGIN
  SELECT EMAIL, GENDER, NAME, PID BULK COLLECT INTO TEST_TABLE_l FROM TEST_TABLE;

  DBMS_OUTPUT.PUT_LINE('Count: ' || TEST_TABLE_l.COUNT);
  FOR rec IN 1 .. TEST_TABLE_l.COUNT
  LOOP
    DBMS_OUTPUT.PUT_LINE('PID: ' || TEST_TABLE_l(rec).PID);
    CASE 'RANGE'
      WHEN 'RANGE' THEN
        IF (TEST_TABLE_l(rec).PID NOT BETWEEN 0 AND 699)
        THEN
          DBMS_OUTPUT.PUT_LINE('Fails range validation');
        END IF;
      ELSE
        DBMS_OUTPUT.PUT_LINE('No type of validation');
    END CASE;

  END LOOP;
END;

Кажется, что проблема заключается в строке

IF ('|| l_table_list ||' (rec). '|| l_validation_columns (vt) .COLUMN_NAME ||' NOT '|| l_validation_columns (vt) .CUSTOM_SQL ||')

Что правильно становится

IF (TEST_TABLE_l (rec) .PID НЕ МЕЖДУ 0 И 699)

Я не уверен, что во время выполнения значениеиз таблицы не переводится правильно или что.Он работает правильно, если поставить «0 И 699» напрямую.

Любое понимание будет полезно, спасибо.

Ответы [ 2 ]

0 голосов
/ 21 декабря 2018

Проблема заключалась в том, что при построении динамического sql

l_validation_columns (vt) .CUSTOM_SQL

иногда было бы нулевым.Это привело к тому, что эта строка выглядела как

IF (TEST_TABLE_l(rec).PID NOT BETWEEN )

Что явно искажено pl / sql.

0 голосов
/ 21 декабря 2018

Я не думаю, что это умный способ сделать.Существуют разные уровни динамического SQL.

Если вы знаете типы столбцов во время разработки, то я бы предложил RefCursor, например:

declare
   cur SYS_REFCURSOR;
   a VARCHAR2(100);
   b VARCHAR2(100);
   c INTEGER;
begin
   OPEN cur FOR 
      'SELECT EMAIL, NAME, PID FROM TEST_TABLE'; -- or any other dynamic string
   LOOP
      FETCH cur INTO a, b, c;
      EXIT WHEN cur%NOTFOUND;
      -- do something with a,b,c
   END LOOP;
   CLOSE cur;
END;

Конечно, вы также можете использовать BULK FETCH.

Если вы не знаете тип столбца во время разработки, вам нужно немного больше кода.Проверьте этот ответ: Как динамически создать переменную с типом данных таблицы?

...