麻烦终止会话然后立即在 Oracle 11g XE 中删除用户

Jak*_*sel 6 oracle oracle-11g-r2

我希望能够终止给定用户的活动会话,然后删除该用户,并在单个数据库调用中执行此操作。这是我到目前为止所拥有的:

DECLARE
cursor c is select *
                 from v$session 
                where username = 'USER_9FFB9';
c_row c%ROWTYPE;
begin
    open C;
    loop
FETCH c INTO c_row;
EXIT WHEN c%NOTFOUND;
        execute immediate 'alter system kill session ''' || 
                 c_row.sid || ',' || c_row.serial# || '''';
    execute immediate 'drop user user_9FFB9 cascade';
    end loop;
end;
/
Run Code Online (Sandbox Code Playgroud)

这样做的问题是,当我在终止会话调用后立即添加 drop user 调用时,我会返回此错误:

ORA-01940: cannot drop a user that is currently connected
Run Code Online (Sandbox Code Playgroud)

但是,如果我在单独的语句中仅等待几秒钟后执行 drop user 语句,它就可以正常工作。我需要做什么才能终止会话并一次性删除用户?v$session 上是否有我应该做的循环,检查它是否仍然打开?

更新 我已经将我的脚本更新为:

DECLARE
cursor c is select *
                 from v$session 
                where username = 'USER_abc';
c_row c%ROWTYPE;
begin
    open C;
    loop
FETCH c INTO c_row;
EXIT WHEN c%NOTFOUND;
        execute immediate 'alter user USER_abc ACCOUNT LOCK';

        execute immediate 'alter system kill session ''' ||
                 c_row.sid || ',' || c_row.serial# || '''';


    end loop;
    DBMS_LOCK.SLEEP(5);
end;

/

drop user user_abc cascade
Run Code Online (Sandbox Code Playgroud)

但是,我仍然收到此错误:

ORA-01940: cannot drop a user that is currently connected
Run Code Online (Sandbox Code Playgroud)

是否有commit我需要添加的类似内容?

完成的脚本

在 Justin Cave 的帮助下,这是我的最终脚本,效果很好:

DECLARE
  l_cnt integer;
BEGIN
  EXECUTE IMMEDIATE 'alter user user_#databaseName# account lock';
  FOR x IN (SELECT *
              FROM v$session
             WHERE username = 'USER_#databaseName#')
  LOOP
    EXECUTE IMMEDIATE 'alter system disconnect session ''' || x.sid || ',' || x.serial# || ''' IMMEDIATE';
  END LOOP;

  -- Wait for as long as it takes for all the sessions to go away
  LOOP
    SELECT COUNT(*)
      INTO l_cnt
      FROM v$session
     WHERE username = 'USER_#databaseName#';
    EXIT WHEN l_cnt = 0;
    dbms_lock.sleep( 2 );
  END LOOP;

  EXECUTE IMMEDIATE 'drop user user_#databaseName# cascade';
END;
Run Code Online (Sandbox Code Playgroud)

显然#databaseName# 是一个简单值的占位符,比如 ABC。谢谢贾斯汀!

Jus*_*ave 8

当您发出 时ALTER SYSTEM KILL SESSION,Oracle 仅将会话标记为已终止并执行异步终止会话的实际工作。这可能只需要几秒钟,如果会话有一个未提交的事务,它做了很多现在必须回滚的工作,或者如果会话需要保持在终止状态以通知客户端,他们的会话已终止。因此,可能需要大量等待才能让用户掉线。

你可以做这样的事情,你的循环将无限期地等待所有会话消失。

DECLARE
  l_cnt integer;
BEGIN
  EXECUTE IMMEDIATE 'alter user abc_user account lock';
  FOR x IN (SELECT *
              FROM v$session
             WHERE username = 'ABC_USER')
  LOOP
    EXECUTE IMMEDIATE 'alter system kill session ''' || x.sid || ',' || x.serial# || '''';
  END LOOP;

  -- Wait for as long as it takes for all the sessions to go away
  LOOP
    SELECT COUNT(*)
      INTO l_cnt
      FROM v$session
     WHERE username = 'ABC_USER';
    EXIT WHEN l_cnt = 0;
    dbms_lock.sleep( 10 );
  END LOOP;

  EXECUTE IMMEDIATE 'drop user abc_user cascade';
END;
Run Code Online (Sandbox Code Playgroud)

不过,一般来说,我会质疑您要解决的问题。我想不出很多次在您决定发布删除时删除具有活动会话的用户是有意义的。在绝大多数情况下,活动会话的存在强烈暗示不应删除该帐户。

  • @JakeFeasel - 我不知道如何让 PMON 更频繁地唤醒,尽管我希望有一个下划线参数(官方未记录)。不过,我不知道这种变化会产生什么后果,尤其是在 XE 上。而且你可能只会通过制作来节省几秒钟。您也可以考虑执行“ALTER SYSTEM DISCONNECT SESSION 'sid,serial#' IMMEDIATE”,但在系统稳定性方面经常这样做会让我感到害怕。 (2认同)