来自 table 的 Oracle 动态 PL/SQL
Oracle Dynamic PL/SQL from table
正在尝试 运行 一些动态 sql 来检查验证。这些验证将是一些要检查的模式,存储在 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;
这会产生错误:
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:
;
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.
*Action:
当我使用 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;
好像是线的问题
IF (' || l_table_list || '(rec).' || l_validation_columns(vt).COLUMN_NAME || ' NOT ' || l_validation_columns(vt).CUSTOM_SQL || ')
正确变成
IF (TEST_TABLE_l(rec).PID NOT BETWEEN 0 AND 699)
我不确定在执行时来自 table 的值是否没有被正确翻译或什么。直接输入“0 AND 699”时确实可以正常工作。
任何见解都会有所帮助,谢谢。
我认为这不是明智的做法。有不同级别的动态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
如果您在设计时不知道列类型,那么您只需要再编写一些代码。检查这个答案:How to dynamically create a variable with the data type of table?
问题是在构建动态 sql
l_validation_columns(vt).CUSTOM_SQL
偶尔会为空。
这导致该行看起来像
IF (TEST_TABLE_l(rec).PID NOT BETWEEN )
这显然是畸形的pl/sql。
正在尝试 运行 一些动态 sql 来检查验证。这些验证将是一些要检查的模式,存储在 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;
这会产生错误:
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: ; 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. *Action:
当我使用 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;
好像是线的问题
IF (' || l_table_list || '(rec).' || l_validation_columns(vt).COLUMN_NAME || ' NOT ' || l_validation_columns(vt).CUSTOM_SQL || ')
正确变成
IF (TEST_TABLE_l(rec).PID NOT BETWEEN 0 AND 699)
我不确定在执行时来自 table 的值是否没有被正确翻译或什么。直接输入“0 AND 699”时确实可以正常工作。
任何见解都会有所帮助,谢谢。
我认为这不是明智的做法。有不同级别的动态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
如果您在设计时不知道列类型,那么您只需要再编写一些代码。检查这个答案:How to dynamically create a variable with the data type of table?
问题是在构建动态 sql
l_validation_columns(vt).CUSTOM_SQL
偶尔会为空。 这导致该行看起来像
IF (TEST_TABLE_l(rec).PID NOT BETWEEN )
这显然是畸形的pl/sql。