ORA-00905 缺少关键字
Posted
技术标签:
【中文标题】ORA-00905 缺少关键字【英文标题】:ORA-00905 missing keyword 【发布时间】:2016-04-28 07:54:58 【问题描述】:我正在尝试通过一个sql文件在sqlplus中执行以下代码:
connect sys/knl_test7 as sysdba
grant sysdba to user1 identified by user1;
grant sysoper to user2 identified by user2;
variable c number;
begin
:c := dbms_sql.open_cursor;
for i in 3 .. 98 loop
dbms_sql.parse(:c, 'grant sysdba to user'||to_char(i)||' identified by user'||to_char(i), dbms_sql.v7);
dbms_lock.sleep(5);
end loop;
dbms_sql.close_cursor(:c);
tkzpwfsync.check_condition('(select count(username) from v$pwfile_users where username like ''USER%'')=98');
end;
/
但是在运行脚本后,我收到以下错误:
begin
*
ERROR at line 1:
ORA-00905 missing keyword
check_condition 过程如下,它主要验证提供的条件是否为真。
create or replace package tkzpwfsync is
procedure check_condition(condition varchar2);
end;
/
show errors
create or replace package body tkzpwfsync is
procedure check_condition(condition varchar2) is
x integer;
c number;
begin
x := 0;
c := dbms_sql.open_cursor;
dbms_sql.parse(c,'select 1 into x from dual where '||condition,dbms_sql.v7);
if (x!=1) then raise_application_error(-20001,'Condition '||condition||' is not met.'); end if;
dbms_sql.close_cursor(c);
end;
end;
/
show errors
create public synonym tkzpwfsync for tkzpwfsync;
grant execute on tkzpwfsync to public;
【问题讨论】:
我可能弄错了 - 但你不是在连接字符串 ([user]/[pwd]@[target DB]) 和 c 变量的“DECLARE”中缺少目标数据库吗?应该是“DECLARE c number”,不是吗? 正是必须合并的内容tkzpwfsync.wait
在做什么?您传递的字符串不是有效的语句,那么它是否将其用作其他动态 SQL 中的条件?不过,错误消息暗示了一些更基本的东西。 (如果您通过 ORACLE_SID 而不是通过 SQL*Net 连接到本地 DB,则不需要提供 @DBNAME;并且如果 PL/SQL 变量会执行异常,则绑定变量很好;并且解析有效,但立即执行更常见)。
如果我排除了wait
电话,您所拥有的一切正常。所以我仍然认为这将是wait
调用产生错误;你显示了整个堆栈跟踪,还是有另一行说ORA-06512: at line 8
?
@Rene 连接命令没问题。也不需要声明。
【参考方案1】:
问题不在于您在匿名块中显示的动态 SQL - 如果可以使用 dbms_sql
来解决此问题,那么使用 dbms_sql
是有效的,但两者都可以使用。并且它与 SQL*Plus 绑定变量声明或用法无关,尽管使用本地 PL/SQL 变量而不是客户端绑定变量(正如您在过程中所使用的那样)更为常见,因为c
的值不需要在匿名块之外知道。但同样有效。
问题在于您致电tkzpwfsync.wait()
。您传递的字符串被用作另一个动态查询的一部分,它是 that 生成的查询抛出 ORA-00905。
使用您的程序,您的程序块会得到比您显示的更多的错误详细信息:
begin
*
ERROR at line 1:
ORA-00905: missing keyword
ORA-06512: at "SYS.DBMS_SQL", line 1199
ORA-06512: at "SCHEMA.TKZPWFSYNC.WAIT", line 7
ORA-06512: at line 8
wait()
过程正在尝试解析 SQL 语句:
select 1 into x from dual
where (select count(username) from v$pwfile_users where username like 'USER%')=98
into x
是一个 PL/SQL 构造,而不是 SQL 的一部分。如果您直接运行它,您会看到相同的 ORA-00905 错误,因为它将“into”视为列别名,然后不知道如何处理“x”:
select 1 into x from dual
where (select count(username) from v$pwfile_users where username like 'USER%')=98;
Error report -
SQL Error: ORA-00905: missing keyword
00905. 00000 - "missing keyword"
您实际上也没有执行查询 - parse
does execute DDL(因此您在匿名块中的授权有效),但不执行 DML。如果满足,那么如果条件不满足,它将得到 ORA-01403;在这里使用聚合更安全,因此您始终可以返回一行。
你可以修改程序来做:
dbms_sql.parse(c,'select count(*) from dual where '||condition,dbms_sql.v7);
dbms_sql.define_column(c, 1, x);
r := dbms_sql.execute(c);
这样就变成了:
procedure wait(condition varchar2) is
x integer;
c number;
r integer;
begin
c := dbms_sql.open_cursor;
dbms_sql.parse(c,'select count(*) from dual where '||condition,dbms_sql.v7);
dbms_sql.define_column(c, 1, x);
r := dbms_sql.execute(c);
if dbms_sql.fetch_rows(c) > 0 then
dbms_sql.column_value(c, 1, x);
end if;
if (x!=1) then
raise_application_error(-20001,'Condition '||condition||' is not met.');
end if;
dbms_sql.close_cursor(c);
end;
/
现在可以了,如果条件不满足,会抛出 ORA-20001。
这里也使用execute immediate
更简单,正如@Motor 所示:
procedure wait(condition varchar2) is
x integer;
begin
execute immediate 'select count(*) from dual where '||condition into x;
if (x!=1) then
raise_application_error(-20001,'Condition '||condition||' is not met.');
end if;
end;
/
...但也许您想使用 dbms_sql.v7
而不是本机执行是有原因的。
我不确定等待是否真的有必要;这些条目将在创建用户时添加到该视图中,因此在您拨打电话之前它们都将存在。使用修改后的过程名称,作为检查而不是等待更有意义,但仍不确定是否有必要 - 如果计数不匹配,则授权失败已经引发错误,或者您已经拥有具有 SYSDBA 权限的用户。所以计数会更高。它似乎不是很有用。 (我什至不会问你为什么需要 98 个具有 SYSDBA 权限的用户;希望你只是在试验,但如果是这样,我会使用一个不那么危险的角色。
【讨论】:
是的,等待的目的是验证条目是否已添加。请在问题中找到上面的等待功能。 @ShekharKanodia - 好的,更新了您的程序抛出该错误的原因(实际上与匿名块无关,任何调用都会失败)和工作版本。我不确定您是否有理由使用dbms_sql.v7
标志指定 Oracle 7 行为;如果不是execute immediate
会简单一些。
感谢您的详细解释。它解决了这个问题。【参考方案2】:
试试这个。希望对您有所帮助。
connect sys/knl_test7@DB_NAME as sysdba
grant sysdba to user1 identified by user1;
grant sysoper to user2 identified by user2;
SET SQLBL ON;
SET DEFINE OFF;
BEGIN
FOR i IN 3 .. 98
LOOP
EXECUTE IMMEDIATE 'grant sysdba to user'||TO_CHAR(i)||' identified by "user'||TO_CHAR(i)||'"';
dbms_lock.sleep(5);
END LOOP;
tkzpwfsync.wait('(select count(username) from v$pwfile_users where username like ''USER%'')=98');
END;
/
【讨论】:
加一个,绝对是EXECUTE IMMEDIATE
的案例
为什么使用execute immediate
比使用dbms_sql.parse()
更好?除了更简单;现在不是一个几乎只是另一个周围的语法糖吗?本地连接不需要@DB_NAME
- 连接不是问题;和 sysdba 连接可能不允许通过 SQL*Net。
@AvrajitRoy 是的 @DB_NAME
不需要。连接也不是 Alex 提到的问题。
如果您可以尝试评论您对过程 wait() 的调用,看看它是否有效。【参考方案3】:
查看 Ask Tom 以获取带有 dbms_sql.parse 的示例
您不要在动态 sql 中选择 ... INTO ...。您只需选择并绑定输出列。
这里是立即执行:
CREATE OR REPLACE PACKAGE BODY POINT_NET.tkzpwfsync
IS
PROCEDURE wait (condition VARCHAR2)
IS
x INTEGER;
BEGIN
x := 0;
EXECUTE IMMEDIATE 'select count(*) from dual where ' || condition INTO x;
IF (x != 1) THEN
raise_application_error (-20001, 'Condition ' || condition || ' is not met.');
END IF;
END;
END;
/
只提一下,这个过程不等待,只检查条件。
【讨论】:
是的,程序已被修改,因此名称 wait 不再有意义。我已经修改了程序的名称。以上是关于ORA-00905 缺少关键字的主要内容,如果未能解决你的问题,请参考以下文章