[英]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.