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