来自dbms_sql.column_value调用的ORA-01007“变量不在选择列表中”

时间:2016-06-07 20:49:21

标签: oracle plsql dynamic-sql

我正在尝试使用动态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

我做错了什么?

2 个答案:

答案 0 :(得分:3)

您发布的代码中存在两个问题。首先,您已跳过the execution flow的部分内容,因为您尚未调用the DEFINE_COLUMN procedure。这就是导致ORA-01007错误的原因,因为动态SQL处理还没有通过该调用告知选择列表列。对于当前代码,您只需要定义第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中已有的describe_columns信息中的数据类型为每列使用适当的类型变量。

当您评论column_value电话时,您遇到的第二个问题仍然存在,一旦该问题得到解决。你的循环没有退出,所以在你从光标中取出最后一行之后你会再进行一次无效的提取,它会抛出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
...

答案 1 :(得分:0)

对于那些在通过 ODP.NET 访问Oracle时发现此问题的人,就像我一样:

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

  1. 运行SELECT * FROM "table"
  2. 在WHERE子句(WHERE ROWNUM < 10)中包含ROWNUM限制。
  3. 通过ODP.NET dataReader.GetSchemaTable()电话运行。
  4. 直接在Oracle SQL Developer上运行不受限制的查询或运行查询似乎不会导致错误。

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

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