简体   繁体   English

表中的Oracle Dynamic PL / SQL

[英]Oracle Dynamic PL/SQL from table

Trying to run some dynamic sql to check validations. 尝试运行一些动态sql来检查验证。 These validations will be some pattern to check against, stored in a table. 这些验证将作为某种模式进行检查,并存储在表中。

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;

This produces the error: 产生错误:

Error report - ORA-06550: line 16, column 47: PLS-00103: Encountered the symbol ")" when expecting one of the following: in like like2 like4 likec between member submultiset ORA-06550: line 26, column 4: PLS-00103: Encountered the symbol "end-of-file" when expecting one of the following: ; 错误报告-ORA-06550:第16行,第47列:PLS-00103:在预期以下情况之一时遇到符号“)”:在成员子多集之间的like like2 like4 likec中ORA-06550:第26行,第4列:PLS- 00103:在预期以下情况之一时遇到符号“文件结束”: The symbol ";" 符号“;” was substituted for "end-of-file" to continue. 被替换为“文件结束”以继续。 ORA-06512: at line 81 06550. 00000 - "line %s, column %s:\\n%s" *Cause: Usually a PL/SQL compilation error. ORA-06512:位于81 06550行。00000-“%s行,%s列:\\ n%s” *原因:通常是PL / SQL编译错误。 *Action: *行动:

When I output the dynamic sql using DBMS_OUTPUT and then run it manually it works correctly. 当我使用DBMS_OUTPUT输出动态sql,然后手动运行它时,它可以正常工作。

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;

The issue seems to be the line 问题似乎是线

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

Which correctly becomes 正确地成​​为

IF (TEST_TABLE_l(rec).PID NOT BETWEEN 0 AND 699) IF(TEST_TABLE_l(rec).PID不在0和699之间)

I'm not sure if at the time of execution the value from the table is not being translated correctly or what. 我不确定在执行时表中的值是否被正确转换或者是什么。 It does work correctly when putting "0 AND 699" in directly. 直接插入“ 0 AND 699”时,它可以正常工作。

Any insight would be helpful, thanks. 任何见解都会有所帮助,谢谢。

I don't think that's a smart way of doing. 我认为这不是明智的做法。 There are different levels dynamic SQL. 动态SQL有不同级别。

If you know the column types at design time then I would suggest a RefCursor like this: 如果您在设计时就知道列的类型,那么我建议使用这样的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;

Of course you can also use BULK FETCH. 当然,您也可以使用BULK FETCH。

If you don't know the column type at design time then you need little more code. 如果您在设计时不知道列类型,则只需要很少的代码。 Check this answer: How to dynamically create a variable with the data type of table? 检查以下答案: 如何动态创建具有表数据类型的变量?

The issue was that when building the dynamic sql 问题是在构建动态sql时

l_validation_columns(vt).CUSTOM_SQL l_validation_columns(VT).CUSTOM_SQL

would occasionally be null. 偶尔为空。 This caused that line to come out looking like 这导致那条线看起来像

IF (TEST_TABLE_l(rec).PID NOT BETWEEN )

Which is obviously malformed pl/sql. 这显然是格式不正确的pl / sql。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM