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 添加列时,我们就开始收到此错误。我不确定导致它失败的所有条件是什么,但我们的条件是:
- 运行一个
SELECT * FROM "table"
.
- 在 WHERE 子句中包含 ROWNUM 限制 (
WHERE ROWNUM < 10
)。
- 运行表示通过ODP.NET调用
dataReader.GetSchemaTable()
。
运行直接在 Oracle 上进行不受限制的查询或 运行 查询 SQL 开发人员似乎没有导致错误。
我过去在使用 Oracle 连接池时遇到过一些非常奇怪的事情,所以我最终认为这可能是问题所在。 解决方案 是重新启动 Web 服务以强制完全删除并重新创建所有连接。
理论是来自连接池的 ODP.NET 连接仍然不知道 table 上存在该列,但该列已由数据库返回。
我正在尝试使用动态 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 添加列时,我们就开始收到此错误。我不确定导致它失败的所有条件是什么,但我们的条件是:
- 运行一个
SELECT * FROM "table"
. - 在 WHERE 子句中包含 ROWNUM 限制 (
WHERE ROWNUM < 10
)。 - 运行表示通过ODP.NET调用
dataReader.GetSchemaTable()
。
运行直接在 Oracle 上进行不受限制的查询或 运行 查询 SQL 开发人员似乎没有导致错误。
我过去在使用 Oracle 连接池时遇到过一些非常奇怪的事情,所以我最终认为这可能是问题所在。 解决方案 是重新启动 Web 服务以强制完全删除并重新创建所有连接。
理论是来自连接池的 ODP.NET 连接仍然不知道 table 上存在该列,但该列已由数据库返回。