Oracle不支持一条REVOKE语句批量撤销用户对整个schema下所有表的权限,必须先查询DBA_TAB_PRIVS等视图获取具体授权记录,再逐条生成并执行REVOKE语句,推荐用PL/SQL游标安全处理。
批量撤销用户所有对象权限必须逐表处理
oracle 不支持一条 revoke 语句直接撤回用户对「整个 schema 下所有表」的全部对象权限。你看到的「批量授权」常靠动态拼 sql 实现,撤销同理——得先查出该用户被授予了哪些具体对象权限,再逐条生成 revoke 语句执行。
常见错误现象是直接写 REVOKE ALL ON ZHANG.* FROM QUERY,这在 Oracle 中语法非法,会报 ORA-00922: missing or invalid option。
实操建议如下:
- 用
DBA_TAB_PRIVS或ALL_TAB_PRIVS查目标用户已有的表级权限(注意视图、序列、过程等需另查) - 过滤条件必须包含
GRANTEE = 'QUERY'和OWNER = 'ZHANG',避免误撤其他 schema 的权限 - 拼出的每条
REVOKE都要带完整对象名:如REVOKE INSERT ON ZHANG.TEST1 FROM QUERY - 不建议用
REVOKE ALL ON ...,因为 Oracle 对ALL的解释受限(仅适用于某些系统对象,且不包含列级权限)
用 PL/SQL 脚本安全生成并执行批量 REVOKE
手动拼 SQL 容易漏、易错,用匿名块+游标更可控。关键点不是“一步删光”,而是“查准再撤”,尤其要避开系统表和 DBA 视图。
以下脚本只处理当前用户(ZHANG)下、被授给 QUERY 的表级 DML 权限(SELECT/INSERT/UPDATE/DELETE),不碰角色、系统权限或列级权限:
DECLARE
v_sql VARCHAR2(500);
BEGIN
FOR r IN (
SELECT 'REVOKE ' || PRIVILEGE || ' ON ' || OWNER || '.' || TABLE_NAME || ' FROM QUERY' stmt
FROM DBA_TAB_PRIVS
WHERE GRANTEE = 'QUERY'
AND OWNER = 'ZHANG'
AND TABLE_NAME IN (SELECT TABLE_NAME FROM DBA_TABLES WHERE OWNER = 'ZHANG')
AND PRIVILEGE IN ('SELECT', 'INSERT', 'UPDATE', 'DELETE')
) LOOP
BEGIN
EXECUTE IMMEDIATE r.stmt;
DBMS_OUTPUT.PUT_LINE('Executed: ' || r.stmt);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Failed: ' || r.stmt || ' -> ' || SQLERRM);
END;
END LOOP;
END;注意:DBA_TAB_PRIVS 需要 DBA 权限;若无,改用 ALL_TAB_PRIVS(但只能看到当前用户有访问权的对象)。
撤销后保留 SELECT 权限的典型场景
业务常要求“回收写权限,但留读权限”。这不是靠单条命令实现的,而是分两步:先全撤,再单独重授 SELECT。
例如,要让 QUERY 只能查 ZHANG 下所有表,不能增删改:
- 先运行上一节脚本,把
INSERT/UPDATE/DELETE全撤掉(SELECT也会被撤) - 再补一条批量授读权限:
SELECT 'GRANT SELECT ON ZHANG.' || TABLE_NAME || ' TO QUERY;' FROM DBA_TABLES WHERE OWNER = 'ZHANG' - 执行结果中的所有
GRANT语句——注意别漏掉新创建的表
不能跳过第一步直接 GRANT SELECT,否则旧的写权限依然存在。
容易忽略的权限残留点
真正难清理干净的,往往不是表,而是这些地方:
-
DBA_ROLE_PRIVS:用户可能被授予了CONNECT、RESOURCE或自定义角色,需单独REVOKE role_name FROM QUERY -
DBA_SYS_PRIVS:如SELECT ANY TABLE这类高危系统权限,一条就能绕过所有表级控制 - 同义词(
SYNONYM):如果QUERY通过同义词访问表,而同义词指向的是其他 schema 的对象,那原表权限可能还在生效 - 物化视图日志、分区表子对象:它们的权限不体现在
DBA_TAB_PRIVS主表中,需额外检查DBA_MVIEWS或DBA_TAB_PARTITIONS
最保险的做法:先用 SELECT * FROM DBA_SYS_PRIVS WHERE GRANTEE = 'QUERY' 和 SELECT * FROM DBA_ROLE_PRIVS WHERE GRANTEE = 'QUERY' 过一遍,再动手撤对象权限。


















