ORA-01007 "variable not in select list" 来自 dbms_sql.column_value 调用

ORA-01007 "variable not in select list" from dbms_sql.column_value call

我正在尝试使用动态 SQL 对具有以下模式的模式中的所有数据进行采样:

DECLARE
  xsql varchar2(5000);


  c           NUMBER;
  d           NUMBER;
  col_cnt     INTEGER;
  f           BOOLEAN;
  rec_tab     DBMS_SQL.DESC_TAB;
  col_num    NUMBER;
  varvar    varchar2(500);

  PROCEDURE print_rec(rec in DBMS_SQL.DESC_REC) IS
  BEGIN
    DBMS_OUTPUT.ENABLE(1000000); 
    DBMS_OUTPUT.NEW_LINE;
    DBMS_OUTPUT.PUT_LINE('col_type            =    '
                         || rec.col_type);
    DBMS_OUTPUT.PUT_LINE('col_maxlen          =    '
                         || rec.col_max_len);
    DBMS_OUTPUT.PUT_LINE('col_name            =    '
                         || rec.col_name);
    DBMS_OUTPUT.PUT_LINE('col_name_len        =    '
                         || rec.col_name_len);
    DBMS_OUTPUT.PUT_LINE('col_schema_name     =    '
                         || rec.col_schema_name);
    DBMS_OUTPUT.PUT_LINE('col_schema_name_len =    '
                         || rec.col_schema_name_len);
    DBMS_OUTPUT.PUT_LINE('col_precision       =    '
                         || rec.col_precision);
    DBMS_OUTPUT.PUT_LINE('col_scale           =    '
                         || rec.col_scale);
    DBMS_OUTPUT.PUT('col_null_ok         =    ');
    IF (rec.col_null_ok) THEN
      DBMS_OUTPUT.PUT_LINE('true');
    ELSE
      DBMS_OUTPUT.PUT_LINE('false');
    END IF;
  END;
BEGIN
  c := DBMS_SQL.OPEN_CURSOR;

    xsql:='  
WITH     got_r_num    AS
(
     SELECT  e.*     -- or whatever columns you want
     ,     ROW_NUMBER () OVER (ORDER BY  dbms_random.value)     AS r_num
     FROM     dba_tab_columns  e
)
SELECT  *     -- or list all columns except r_num
FROM     got_r_num
WHERE     r_num     <= 10';


  DBMS_SQL.PARSE(c, xsql, DBMS_SQL.NATIVE);

  d := DBMS_SQL.EXECUTE(c);

  DBMS_SQL.DESCRIBE_COLUMNS(c, col_cnt, rec_tab);

     LOOP 
       IF DBMS_SQL.FETCH_ROWS(c)>0 THEN 
         NULL;
         -- get column values of the row 
         DBMS_SQL.COLUMN_VALUE(c, 2, varvar); 
         --dbms_output.put_line('varvar=');
         --DBMS_SQL.COLUMN_VALUE(source_cursor, 2, name_var); 
         --DBMS_SQL.COLUMN_VALUE(source_cursor, 3, birthdate_var); 

  -- Bind the row into the cursor that inserts into the destination table. You 
  -- could alter this example to require the use of dynamic SQL by inserting an 
  -- if condition before the bind. 
        --DBMS_SQL.BIND_VARIABLE(destination_cursor, ':id_bind', id_var); 
        --DBMS_SQL.BIND_VARIABLE(destination_cursor, ':name_bind', name_var); 
        --DBMS_SQL.BIND_VARIABLE(destination_cursor, ':birthdate_bind', 
--birthdate_var); 
        --ignore := DBMS_SQL.EXECUTE(destination_cursor); 
      --ELSE 

  -- No more rows to copy: 
        --EXIT; 
      END IF; 
    END LOOP; 



    --EXIT WHEN d != 10;
  --END LOOP;


  col_num := rec_tab.first;
  IF (col_num IS NOT NULL) THEN
    LOOP
      print_rec(rec_tab(col_num));
      col_num := rec_tab.next(col_num);
      EXIT WHEN (col_num IS NULL);
    END LOOP;
  END IF;

  DBMS_SQL.CLOSE_CURSOR(c);
END;
/

当我 运行 它从 dbms_sql.column_value 调用的行中给我这个错误:

ORA-01007: variable not in select list

如果我注释掉 dbms_sql.column_value 调用它仍然会出错,但现在:

ORA-01002: fetch out of sequence

我做错了什么?

您发布的代码有两个问题。首先,您跳过了 the execution flow because you haven't called the DEFINE_COLUMN procedure 的一部分。这就是导致 ORA-01007 错误的原因,因为尚未通过该调用告知动态 SQL 处理有关 select 列表列的信息。对于您当前的代码,您只需要定义第 2 列,但假设您实际上想要引用其他代码,您可以在循环中定义它们。要将它们全部视为要显示的字符串,您可以这样做:

...
  DBMS_SQL.PARSE(c, xsql, DBMS_SQL.NATIVE);

  d := DBMS_SQL.EXECUTE(c);

  DBMS_SQL.DESCRIBE_COLUMNS(c, col_cnt, rec_tab);

  FOR i IN 1..col_cnt
  LOOP
    -- dbms_output.put_line('col_name is ' || rec_tab(i).col_name);
    DBMS_SQL.DEFINE_COLUMN(c, i, varvar, 500);
  END LOOP;

  LOOP
    IF DBMS_SQL.FETCH_ROWS(c)>0 THEN
    ...

如果您想做任何需要将变量视为正确类型的事情,您可以拥有每种类型的局部变量,并使用来自 rec_tab 信息的数据类型,您已经从 [=15] =] 为每一列使用适当类型的变量。

第二个问题,您在评论 column_value 调用时遇到的问题,在解决 definbe 问题后仍然存在。您的循环永远不会退出,因此在您从游标中获取最后一行之后,您将进行进一步的无效获取,这将引发 ORA-01002。您已经有了避免这种情况的代码,但它被注释掉了:

...
  LOOP 
    IF DBMS_SQL.FETCH_ROWS(c)>0 THEN 
      -- get column values of the row 
      DBMS_SQL.COLUMN_VALUE(c, 2, varvar); 
      ...
    ELSE 
      -- No more rows to copy: 
      EXIT; 
    END IF; 
  END LOOP; 
...

通过这两项更改,您的代码将运行并转储视图结构:

PL/SQL procedure successfully completed.

col_type            =    1
col_maxlen          =    30
col_name            =    OWNER
col_name_len        =    5
col_schema_name     =    
col_schema_name_len =    0
col_precision       =    0
col_scale           =    0
col_null_ok         =    false

col_type            =    1
col_maxlen          =    30
col_name            =    TABLE_NAME
...

对于那些像我一样通过ODP.NET访问Oracle时发现这个问题的人:

每当我们向应用程序中的现有 table 添加列时,我们就开始收到此错误。我不确定导致它失败的所有条件是什么,但我们的条件是:

  1. 运行一个SELECT * FROM "table".
  2. 在 WHERE 子句中包含 ROWNUM 限制 (WHERE ROWNUM < 10)。
  3. 运行表示通过ODP.NET调用dataReader.GetSchemaTable()

运行直接在 Oracle 上进行不受限制的查询或 运行 查询 SQL 开发人员似乎没有导致错误。

我过去在使用 Oracle 连接池时遇到过一些非常奇怪的事情,所以我最终认为这可能是问题所在。 解决方案 是重新启动 Web 服务以强制完全删除并重新创建所有连接。

理论是来自连接池的 ODP.NET 连接仍然不知道 table 上存在该列,但该列已由数据库返回。