Oracle - BULK COLLECT INTO VARRAY 与绑定变量一起使用,仅收集列 headers
Oracle - BULK COLLECT INTO VARRAY used with Bind Variables only collecting column headers
快速免责声明:
首先,我知道在 Oracle 中处理动态 SQL 的首选方法是 DBMS_SQL 包,但不幸的是,我的应用程序团队目前没有执行这些过程的授权,我我希望在我们的 DBA 团队回复我之前解决这个快速的解决方法。此外,此数据库位于 Oracle 12c.
脚本目标:我最近开发了一个存储过程(我们称之为原始),它使用“控制table" 对具有许多模式和 table 的数据库中的某些列进行大量更新。我现在正在努力使用的这个脚本(我们称之为 Test)旨在快速循环遍历受 Original 影响的那些列,以便验证一切都按预期进行。最后,我想输出每个更改列的前 5 个结果,并将假脱机文件交给我的测试团队进行验证。
两个脚本中使用的 control_table
有 4 列,如下所示:
OWNER
TABLE_NAME
COLUMN_NAME
ALGORITHM
Schema1
TableA
ColumnA
Method1
Schema1
TableB
ColumnB
Method1
Schema2
TableC
ColumnC
Method2
由 Original 更新的 table 之一的示例(假设上面的 TableA
)是:
OtherCol1
OtherCol2
ColumnA
OtherCol3
Ignored
Ignored
UpdatedData1
Ignored
Ignored
Ignored
UpdatedData2
Ignored
Ignored
Ignored
UpdatedData3
Ignored
测试 脚本 问题:我有动态 SQL - 我相信 - 按需要工作,我有一直在试图弄清楚如何最好地打印 EXECUTE IMMEDIATE
命令的结果进行输出。在做一些阅读时,我发现 BULK COLLECT INTO should allow me to store the results of the dynamic queries into a COLLECTION 然后我可以用 dbms_output
打印。我试图用 TABLE 和 VARRAY 来做到这一点,但在这两种情况下 当我打印时,我发现存储在我的 collection 中的数据是列 header 我的动态查询而不是查询值! 我唯一认为可能是问题的是 BULK COLLECT INTO
与 USING
命令的组合,当我 运行 动态语句,但我在文档中没有看到任何内容表明这两个命令不兼容,我下面的 Test 程序编译没有问题(甚至似乎 运行 ok ).
测试 脚本:
SET SERVEROUTPUT ON SIZE UNLIMITED;
DECLARE
l_script VARCHAR2(500);
l_errm VARCHAR2(64);
TYPE results IS VARRAY(5) OF VARCHAR2(250);
va_cols results; --Defining here with a VARRAY but I have also tried with a table
BEGIN
FOR c_col IN(
SELECT owner, table_name, column_name, algorithm FROM control_list)
LOOP
l_errm := NULL;
va_cols := NULL;
BEGIN
dbms_output.put_line('Column '|| c_col.column_name || ' of table ' || c_col.owner ||
'.' || c_col.table_name || ' used algorithm ' || c_col.algorithm);
l_script := 'SELECT :1 FROM ' || c_col.owner || '.' || c_col.table_name ||
' WHERE :2 IS NOT NULL FETCH FIRST 5 ROWS ONLY';
dbms_output.put_line('Script sent to Exec Immediate: ' || l_script); --Print l_script for debugging
EXECUTE IMMEDIATE l_script BULK COLLECT INTO va_cols USING c_col.column_name, c_col.column_name;
dbms_output.put_line(va_cols(1));
dbms_output.put_line(va_cols(2));
dbms_output.put_line(va_cols(3));
dbms_output.put_line(va_cols(4));
dbms_output.put_line(va_cols(5));
EXCEPTION
WHEN OTHERS THEN
l_errm := SUBSTR(SQLERRM, 1, 64);
dbms_output.put_line(' ERROR: ' || l_errm || '. Skipping row');
CONTINUE;
END;
END LOOP;
END;
/
所以我上面脚本的 dbms_output 是:
Column ColumnA of table Schema1.TableA used algorithm Method1
Script sent to Exec Immediate: SELECT :1 FROM SCHEMA1.TABLEA WHERE :2 IS NOT NULL FETCH FIRST 5 ROWS ONLY
UpdatedData1
UpdatedData2
UpdatedData3
UpdatedData4
UpdatedData5
相反,奇怪的是,当我 运行 这是:
Column ColumnA of table Schema1.TableA used algorithm Method1
Script sent to Exec Immediate: SELECT :1 FROM SCHEMA1.TABLEA WHERE :2 IS NOT NULL FETCH FIRST 5 ROWS ONLY
ColumnA
ColumnA
ColumnA
ColumnA
ColumnA
有没有人以前看过这个并且知道我做错了什么?提前致谢!!
您不能使用绑定变量来更改您引用的列。您使用绑定变量在运行时指定特定值。当你这样做时
l_script := 'SELECT :1 FROM ' || c_col.owner || '.' || c_col.table_name ||
' WHERE :2 IS NOT NULL FETCH FIRST 5 ROWS ONLY';
EXECUTE IMMEDIATE l_script BULK COLLECT INTO va_cols USING c_col.column_name, c_col.column_name;
您告诉 Oracle 您想要 select 变量 c_col.column_name
中的文字字符串。不是 table 中该名称的列。这就是为什么每一行 returns 这个字面值。
您需要使用列名动态 assemble SQL 语句,而不是尝试将它们用作绑定变量。所以像
l_script := 'SELECT ' || c_col.column_name ||
' FROM ' || c_col.owner || '.' || c_col.table_name ||
' WHERE ' || c_col.column_name || ' IS NOT NULL FETCH FIRST 5 ROWS ONLY';
EXECUTE IMMEDIATE l_script BULK COLLECT INTO va_cols;
这大概就是您想要的。我将光标放在表和列上以检查生成动态 SQL.
内循环从上一个查询中读取列值
DECLARE
TYPE CurTyp IS REF CURSOR;
v_cursor CurTyp;
v_value VARCHAR2(200);
v_stmt_str VARCHAR2(200);
BEGIN
FOR c IN (
SELECT table_name, column_name FROM control_list)
LOOP
dbms_output.put_line('tab: '||c.table_name);
v_stmt_str := 'SELECT '||c.column_name||' FROM '|| c.table_name;
OPEN v_cursor FOR v_stmt_str;
LOOP
FETCH v_cursor INTO v_value;
EXIT WHEN v_cursor%NOTFOUND;
dbms_output.put_line('col: '||c.column_name||' val: '||v_value);
END LOOP;
END LOOP;
CLOSE v_cursor;
END;
/
快速免责声明: 首先,我知道在 Oracle 中处理动态 SQL 的首选方法是 DBMS_SQL 包,但不幸的是,我的应用程序团队目前没有执行这些过程的授权,我我希望在我们的 DBA 团队回复我之前解决这个快速的解决方法。此外,此数据库位于 Oracle 12c.
脚本目标:我最近开发了一个存储过程(我们称之为原始),它使用“控制table" 对具有许多模式和 table 的数据库中的某些列进行大量更新。我现在正在努力使用的这个脚本(我们称之为 Test)旨在快速循环遍历受 Original 影响的那些列,以便验证一切都按预期进行。最后,我想输出每个更改列的前 5 个结果,并将假脱机文件交给我的测试团队进行验证。
两个脚本中使用的 control_table
有 4 列,如下所示:
OWNER | TABLE_NAME | COLUMN_NAME | ALGORITHM |
---|---|---|---|
Schema1 | TableA | ColumnA | Method1 |
Schema1 | TableB | ColumnB | Method1 |
Schema2 | TableC | ColumnC | Method2 |
由 Original 更新的 table 之一的示例(假设上面的 TableA
)是:
OtherCol1 | OtherCol2 | ColumnA | OtherCol3 |
---|---|---|---|
Ignored | Ignored | UpdatedData1 | Ignored |
Ignored | Ignored | UpdatedData2 | Ignored |
Ignored | Ignored | UpdatedData3 | Ignored |
测试 脚本 问题:我有动态 SQL - 我相信 - 按需要工作,我有一直在试图弄清楚如何最好地打印 EXECUTE IMMEDIATE
命令的结果进行输出。在做一些阅读时,我发现 BULK COLLECT INTO should allow me to store the results of the dynamic queries into a COLLECTION 然后我可以用 dbms_output
打印。我试图用 TABLE 和 VARRAY 来做到这一点,但在这两种情况下 当我打印时,我发现存储在我的 collection 中的数据是列 header 我的动态查询而不是查询值! 我唯一认为可能是问题的是 BULK COLLECT INTO
与 USING
命令的组合,当我 运行 动态语句,但我在文档中没有看到任何内容表明这两个命令不兼容,我下面的 Test 程序编译没有问题(甚至似乎 运行 ok ).
测试 脚本:
SET SERVEROUTPUT ON SIZE UNLIMITED;
DECLARE
l_script VARCHAR2(500);
l_errm VARCHAR2(64);
TYPE results IS VARRAY(5) OF VARCHAR2(250);
va_cols results; --Defining here with a VARRAY but I have also tried with a table
BEGIN
FOR c_col IN(
SELECT owner, table_name, column_name, algorithm FROM control_list)
LOOP
l_errm := NULL;
va_cols := NULL;
BEGIN
dbms_output.put_line('Column '|| c_col.column_name || ' of table ' || c_col.owner ||
'.' || c_col.table_name || ' used algorithm ' || c_col.algorithm);
l_script := 'SELECT :1 FROM ' || c_col.owner || '.' || c_col.table_name ||
' WHERE :2 IS NOT NULL FETCH FIRST 5 ROWS ONLY';
dbms_output.put_line('Script sent to Exec Immediate: ' || l_script); --Print l_script for debugging
EXECUTE IMMEDIATE l_script BULK COLLECT INTO va_cols USING c_col.column_name, c_col.column_name;
dbms_output.put_line(va_cols(1));
dbms_output.put_line(va_cols(2));
dbms_output.put_line(va_cols(3));
dbms_output.put_line(va_cols(4));
dbms_output.put_line(va_cols(5));
EXCEPTION
WHEN OTHERS THEN
l_errm := SUBSTR(SQLERRM, 1, 64);
dbms_output.put_line(' ERROR: ' || l_errm || '. Skipping row');
CONTINUE;
END;
END LOOP;
END;
/
所以我上面脚本的 dbms_output 是:
Column ColumnA of table Schema1.TableA used algorithm Method1
Script sent to Exec Immediate: SELECT :1 FROM SCHEMA1.TABLEA WHERE :2 IS NOT NULL FETCH FIRST 5 ROWS ONLY
UpdatedData1
UpdatedData2
UpdatedData3
UpdatedData4
UpdatedData5
相反,奇怪的是,当我 运行 这是:
Column ColumnA of table Schema1.TableA used algorithm Method1
Script sent to Exec Immediate: SELECT :1 FROM SCHEMA1.TABLEA WHERE :2 IS NOT NULL FETCH FIRST 5 ROWS ONLY
ColumnA
ColumnA
ColumnA
ColumnA
ColumnA
有没有人以前看过这个并且知道我做错了什么?提前致谢!!
您不能使用绑定变量来更改您引用的列。您使用绑定变量在运行时指定特定值。当你这样做时
l_script := 'SELECT :1 FROM ' || c_col.owner || '.' || c_col.table_name ||
' WHERE :2 IS NOT NULL FETCH FIRST 5 ROWS ONLY';
EXECUTE IMMEDIATE l_script BULK COLLECT INTO va_cols USING c_col.column_name, c_col.column_name;
您告诉 Oracle 您想要 select 变量 c_col.column_name
中的文字字符串。不是 table 中该名称的列。这就是为什么每一行 returns 这个字面值。
您需要使用列名动态 assemble SQL 语句,而不是尝试将它们用作绑定变量。所以像
l_script := 'SELECT ' || c_col.column_name ||
' FROM ' || c_col.owner || '.' || c_col.table_name ||
' WHERE ' || c_col.column_name || ' IS NOT NULL FETCH FIRST 5 ROWS ONLY';
EXECUTE IMMEDIATE l_script BULK COLLECT INTO va_cols;
这大概就是您想要的。我将光标放在表和列上以检查生成动态 SQL.
内循环从上一个查询中读取列值
DECLARE
TYPE CurTyp IS REF CURSOR;
v_cursor CurTyp;
v_value VARCHAR2(200);
v_stmt_str VARCHAR2(200);
BEGIN
FOR c IN (
SELECT table_name, column_name FROM control_list)
LOOP
dbms_output.put_line('tab: '||c.table_name);
v_stmt_str := 'SELECT '||c.column_name||' FROM '|| c.table_name;
OPEN v_cursor FOR v_stmt_str;
LOOP
FETCH v_cursor INTO v_value;
EXIT WHEN v_cursor%NOTFOUND;
dbms_output.put_line('col: '||c.column_name||' val: '||v_value);
END LOOP;
END LOOP;
CLOSE v_cursor;
END;
/