ORA-00905缺少关键字

时间:2016-04-28 07:54:58

标签: database oracle plsql sqlplus

我试图通过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;

3 个答案:

答案 0 :(得分:1)

试试这个。希望它有所帮助。

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;
    /

答案 1 :(得分:1)

问题不在于您在匿名区块中显示的动态SQL - 如果可能的话,可以使用dbms_sql来识别它是否有效(如果可能不常见)与execute immediate但是有效。并且它不是SQL * Plus绑定变量声明或用法,尽管再次使用本地PL / SQL变量而不是客户端绑定变量更常见(正如您在过程中所做的那样),因为c的值不需要在匿名块之外知道。但是要么再次起作用。

问题在于您致电tkzpwfsync.wait()。您传递的字符串将用作另一个动态查询的一部分,而 生成的查询正在抛出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 conmstruct,不是SQL的一部分。如果你直接运行它,你会看到相同的ORA-00905错误,因为它看到'进入'作为列别名,然后不知道如何处理' 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也比较简单,正如@Mottor所示:

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权限的用户;希望你只是尝试,但如果是这样的话,我会使用一个不那么危险的角色。)

答案 2 :(得分:0)

使用dbms_sql.parse

查看Ask Tom示例
  

你没有SELECT ... INTO ...在动态sql中。您只需选择并绑定输出列。

以下是EXECUTE IMMEDIATE:

paramMap.get("userId")

仅提及,此程序不等待,只检查条件。