在基于 FOR 循环的 SQL 查询中使用 PLSQL 变量

Using PLSQL variables in SQL Query based on FOR LOOP

我正在尝试使用 PLSQL 执行一些 DML 查询。我已经声明了一个有 5 个 table 名称的数组,我想 运行 它在 FOR LOOP 下。此时我的代码如下所示:

DECLARE
  c_exist NUMBER;
  test_array dbms_sql.varchar2_table; 
BEGIN 
 test_array(1) := 'TEST_1';
 test_array(2) := 'TEST_2';
FOR i IN test_array.FIRST .. test_array.LAST
 LOOP
  SELECT count(*) INTO c_exist from user_tables WHERE table_name = test_array(i); 
  IF c_exist = 1 THEN 
   EXECUTE IMEDIATE 'ALTER TABLE ' || test_array(i) || 'COMPRESS'; 
  END IF; 
 END LOOP; 
END;

问题是脚本试图读取 SELECT 语句下的 test_array 变量。我收到 ORA-06512 ERROR。在我看来,脚本语法很好。它在没有 SELECT stamtent 的情况下通过了测试,但只需使用 dbms_output.put_line(test_array(i)) 它就通过了测试。

整个错误堆栈:

ORA-06502: 
ORA-06512: 
06502. 00000 -  "PL/SQL: numeric or value error%s"
*Cause:    An arithmetic, numeric, string, conversion, or constraint error
           occurred. For example, this error occurs if an attempt is made to
           assign the value NULL to a variable declared NOT NULL, or if an
           attempt is made to assign an integer larger than 99 to a variable
           declared NUMBER(2).
*Action:   Change the data, how it is manipulated, or how it is declared so
           that values do not violate constraints.

此外,类似的东西工作得很好,dbms_output 打印正确:

DECLARE
  c_exist NUMBER;
  test_array dbms_sql.varchar2_table; 
BEGIN 
 test_array(1) := 'TEST_1';
 test_array(2) := 'TEST_2';
FOR i IN test_array.FIRST .. test_array.LAST
 LOOP
  dbms_output.put_line(test_array(i)); 
 END LOOP; 
END;

您在动态查询中的语法不正确。

COMPRESS 关键字之前缺少一个 space。

您的代码:

EXECUTE IMEDIATE 'ALTER TABLE ' || test_array(i) || 'COMPRESS';  

应该是代码:

EXECUTE IMEDIATE 'ALTER TABLE ' || test_array(i) || ' COMPRESS'; 

您拼写错误 IMMEDIATE,需要在 table 名称和 COMPRESS 之间添加一个 space。

DECLARE
  c_exist NUMBER;
  test_array dbms_sql.varchar2_table; 
BEGIN 
  test_array(1) := 'TEST_1';
  test_array(2) := 'TEST_2';
  FOR i IN test_array.FIRST .. test_array.LAST LOOP
    SELECT count(*)
    INTO   c_exist
    from   user_tables
    WHERE table_name = test_array(i); 
  
    IF c_exist = 1 THEN 
      EXECUTE IMMEDIATE 'ALTER TABLE ' || test_array(i) || ' COMPRESS';
      DBMS_OUTPUT.PUT_LINE( test_array(i) || ' compressed.' );
    END IF; 
  END LOOP; 
END;
/

此外,您不需要检查 table 是否存在;只需尝试压缩 table 并捕获异常:

DECLARE
  table_not_exists EXCEPTION;
  test_array dbms_sql.varchar2_table;
  i          BINARY_INTEGER;
  
  PRAGMA EXCEPTION_INIT( table_not_exists, -942 );
BEGIN 
  test_array(1) := 'TEST_1';
  test_array(3) := 'TEST_2';
  i := test_array.FIRST;
  WHILE i IS NOT NULL LOOP
    BEGIN
      EXECUTE IMMEDIATE 'ALTER TABLE ' || test_array(i) || ' COMPRESS';
      DBMS_OUTPUT.PUT_LINE( test_array(i) || ' compressed.' );
    EXCEPTION
      WHEN table_not_exists THEN
        DBMS_OUTPUT.PUT_LINE( test_array(i) || ' ' || SQLERRM );
    END;
    i := test_array.NEXT(i);
  END LOOP;
END;
/

(此外,dbms_sql.varchar2_table 是一个关联数组,可能是稀疏的,因此您不应假设 FIRSTLAST 之间的每个索引都存在。)

那么,如果你有 table:

CREATE TABLE test_1 ( id NUMBER );

上面的 PL/SQL 块输出:

TEST_1 compressed.
TEST_2 ORA-00942: table or view does not exist

db<>fiddle here