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 INTOUSING 命令的组合,当我 运行 动态语句,但我在文档中没有看到任何内容表明这两个命令不兼容,我下面的 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;
/