数据字典不是「给你查的表」,是「系统自己维护的表」。你建一张表,它往字典里写一条; 你加个索引,再写一条。所以字典里永远只有「已经发生的事实」,没有你的意图。
这一篇是第 2 章的收尾,
也是全书最实用的一篇——后面每一章查「有没有、在哪、多大、谁在用」,都是查字典。
它同时是「三层前缀」的说明书:user_ 看自己、all_ 看有权、dba_ 看全库。
这个三层结构在第 12 章讲权限时会再出现一次。
一、三层前缀:同一件事,三种视野
| 前缀 | 范围 | 谁能查 |
|---|---|---|
user_ |
当前用户自己拥有的对象 | 所有人 |
all_ |
当前用户有权访问的对象(含别人的) | 所有人 |
dba_ |
全库所有对象 | 要有 DBA 权限 |
v$ |
动态性能视图:内存与运行状态 | 要有相应权限 |
同一个对象在三个前缀下都有记录,只是可见范围不同。
user_tables 和 dba_tables 的结构几乎一样——
因为底层是同一张基表,只是视图上加了过滤条件。
v$ 那一类要单独记:它们不是表,是内存结构的映射。
实例没起来就查不到(1.5 节演过)。
二、最常用的那几个视图
| 要看什么 | 查哪个 | 说明 |
|---|---|---|
| 有哪些表 | user_tables |
含行数、块数、最后分析时间 |
| 表有哪些列 | user_tab_columns |
类型、长度、是否可空 |
| 有哪些索引 | user_indexes/user_ind_columns |
索引在哪些列上 |
| 有哪些约束 | user_constraints |
主键、外键、检查 |
| 当前会话 | v$session |
谁连着、在等什么 |
| 正在跑的 SQL | v$sql |
文本、执行次数、逻辑读 |
这几张表能回答 DBA 的日常问题: 「这张表多大」「这个索引有没有人用」「现在有几个会话在等锁」—— 答案全在字典里,不用去翻数据。
三、为什么字典不能随便改
字典是系统维护的,你不该 update 它。改字典等于骗系统:
告诉它「这张表有 8 列」,而实际有 9 列,之后所有依赖字典的操作都会错。
官方给的唯一「改字典」的正道是 DDL——alter table、create index。
DDL 会同时改数据字典和实际结构,两边保持一致。
有些老资料会教你 update sys.obj$ 之类——那是拿库开玩笑,改完可能连启动都起不来。别学。
四、desc 不是 SQL
教材里天天敲 desc emp,但它是 SQL*Plus 命令,不是 SQL 语句。
所以它不带分号、也不能在 JDBC 里执行。
想在程序里看表结构,得查字典:
select column_name, data_type from user_tab_columns where table_name='EMP'。
这条才是 SQL,能塞进任何客户端。
这个区别在 1.4 节提过一次,这里再钉一遍:
SQL*Plus 命令是「工具的命令」,SQL 是「数据库的语言」。
换工具,前者失效,后者照样跑。
五、动手 Demo:用 SQLite 的字典查自己
SQLite 的字典只有一层:sqlite_master。
但它足够把「查字典」这件事演完整——建视图、建索引,再看字典里怎么记的。
最后一段是「用字典查字典」:
表名从 sqlite_master 来,列数从 pragma_table_info 来,两边对一下。
con = sqlite3.connect(':memory:') # 里面是 scott 四表
con.execute('CREATE VIEW v_emp AS SELECT empno, ename, sal FROM emp WHERE sal > 1000')
con.execute('CREATE INDEX idx_ename ON emp (ename)')
# 1 这个库里有什么:一张表全记着
con.execute('select type, count(*) from sqlite_master group by type').fetchall()
# 2 看表结构(相当于 DESC)
con.execute("select cid, name, type, [notnull], pk from pragma_table_info('emp')").fetchall()
# 3 看索引
con.execute("pragma index_list('emp')").fetchall()
con.execute("pragma index_info('idx_ename')").fetchall()
# 4 用字典查字典:每张表几列
con.execute('select m.name, (select count(*) from pragma_table_info(m.name)) '
'from sqlite_master m where m.type = \'table\'').fetchall()
实跑输出:
--------------------------------------------------------------------
第十课:数据字典——数据库自己的那本账
--------------------------------------------------------------------
**数据字典不是「给你查的表」,是「系统自己维护的表」。**
**你建一张表,它往字典里写一条记录;你加个索引,再写一条。**
**所以字典里的内容永远是「已经发生的事实」,不是你的意图。**
三层前缀:谁看得到什么
--------------------------------------------------------------------
SQL> select pre, scope, who from pre
PRE SCOPE WHO
--------------------------------------------------------------
user_ 当前用户自己的对象 所有人都有
all_ 当前用户有权访问的对象(含别人的) 所有人都有
dba_ 全库所有对象 **只有 DBA 权限能查**
v$ 动态性能视图:内存与运行状态 需要相应权限
已选择 4 行
**记忆法:**
**`user_` 是我的、`all_` 是我能碰的、`dba_` 是全部的。**
**同一个东西在三个前缀下都有一条,只是可见范围不同。**
SQLite 的字典长什么样
--------------------------------------------------------------------
**SQLite 只有一个「全库」层,就是 `sqlite_master`:**
**一张普通的表,五列——类型、名字、所属表、根页号、建它的那句 SQL。**
SQL> select type, count(*) as n from sqlite_master group by type
TYPE N
-------
index 1
table 4
view 1
已选择 3 行
SQL> select type, name, tbl_name from sqlite_master where type in ('view','index') order by name
TYPE NAME TBL_NAME
------------------------
index idx_ename emp
view v_emp v_emp
已选择 2 行
看表结构:DESCRIBE 的等价物
--------------------------------------------------------------------
教材里天天敲 `desc emp`,**它在 SQLite 里就是 `pragma_table_info`:**
SQL> select cid, name, type, "notnull", pk from pragma_table_info('emp')
CID NAME TYPE NOTNULL PK
-------------------------------
0 empno INTEGER 0 1
1 ename TEXT 1 0
2 job TEXT 0 0
3 mgr INTEGER 0 0
4 hiredate TEXT 0 0
5 sal REAL 0 0
6 comm REAL 0 0
7 deptno INTEGER 0 0
已选择 8 行
[Oracle 原码 · 本沙箱不执行]
| DESCRIBE emp;
Oracle 的 desc 是 SQL*Plus 命令,不是 SQL,所以不带分号也行。
**注意 `pragma_table_info` 能当表来查**——
**可以 `where`、可以 `join`、可以算统计。**
**Oracle 里对应的写法是 `select * from user_tab_columns where table_name='EMP'`,一样能筛能连。**
看索引:DBA 查的是什么
--------------------------------------------------------------------
SQL> pragma index_list('emp')
SEQ NAME UNIQUE ORIGIN PARTIAL
-----------------------------------
0 idx_ename 0 c 0
已选择 1 行
**五列:序号、索引名、是否唯一、来源、是否部分索引。**
**「来源」那一列最有意思:`c` 是 `create index` 建的(就是这条),`u` 是建表时写 `unique` 自动建的。**
SQL> pragma index_info('idx_ename')
SEQNO CID NAME
---------------
0 1 ename
已选择 1 行
[Oracle 原码 · 本沙箱不执行]
| SELECT index_name, column_name, column_position
| FROM user_ind_columns WHERE table_name = 'EMP';
一条查询把「字典能干什么」说清
--------------------------------------------------------------------
SQL> select m.type, m.name, (select count(*) from pragma_table_info(m.name)) as cols from sqlite_master m where m.type = 'table' order by m.name
TYPE NAME COLS
-------------------
table bonus 4
table dept 3
table emp 8
table salgrade 3
已选择 4 行
**这就是「用字典查字典」:**
**表名从 `sqlite_master` 来,列数从 `pragma_table_info` 来,两边对得上。**
**DBA 日常干的事有一大半是这个:**
**「这个库有多少张表、多少索引、哪张表最大、哪个索引没人用」**——**全是查字典,不是查数据。**
**一条提醒:**
**别在业务 SQL 里直接依赖 `dba_` 视图。**
**它们结构会随版本变,而且查全库很重。** **要看结构用 `user_`,要看运行状态用 `v$`。**
sqlite_master 只有五列:类型、名字、所属表、根页号、建它的那句 SQL。
最后那列最有价值——建表语句原文都在里面,
相当于 Oracle 的 user_tables 加 user_views 加 user_indexes 三合一。
pragma_table_info 能当表查(where、join、子查询都行),这是它比 desc 强的地方:
desc 只给你看,pragma_table_info 能参与计算。
pragma index_list 的「来源」列值得记:
c 是 create index 建的,u 是 unique 约束自动建的,pk 是主键自动建的。
Oracle 里查 user_indexes.index_type 和 generated 列,看的是同一件事。
最后那条「每张表几列」是这一篇的重点:
DBA 干的活一大半是这种「查结构、算统计」,不是「查数据」。
所以会写这种嵌套查询,比会写业务查询更接近运维。
30 秒实验
- 给
emp加一个unique约束,再看pragma index_list的「来源」列——会多出一条u。 - 查
sqlite_master里的sql列,把emp的建表语句原样打出来。 这就是「库自己记得你要建什么」。 - 把
pragma_table_info换成pragma_index_info,查索引建在哪一列上。 再看pragma_index_list的结果,两张一对,索引结构就出来了。
常见误区
- 在业务代码里
select * from dba_tables。 查全库很重,而且dba_视图结构会随版本变。 看自己的用user_。 - 以为
desc是 SQL。 它是 SQLPlus 的命令,JDBC、Navicat、Python 驱动里都用不了*, 只能用字典视图代替。 - 直接
update数据字典。 网上有些「修复」教程教你改sys.obj$, 那是骗系统,改完可能起不来。 正统做法只有 DDL。
自测三问(点开对答案)
1. `user_tables`、`all_tables`、`dba_tables` 三者的结构为什么几乎一样? —— **因为底层是同一张基表**,三个视图只是加了不同的过滤条件: **`user_` 过滤「owner 是我」,`all_` 过滤「我有权限」,`dba_` 不过滤。** 2. 为什么 `v$` 开头的视图在实例没起来时查不到? —— **它们不是表,是内存结构的映射。** 实例没起来,内存结构不存在, **自然查不到**(报 `ORA-01034` 或类似错误)。 3. 想在程序里看表结构,用什么? —— **查字典:`user_tab_columns`。** `desc` 是 SQL*Plus 的命令,**程序里没有这个命令**, 只有 SQL 能用。第 2 章到这里结束。 下一篇进第 3 章:SQL*Plus 命令——第 3.1 节先讲「SQLPlus 和数据库怎么交互」, 把「本地连接」和「通过网络连接」这两条路拆开。*
- ☐ (本篇完,下一篇 3.1 SQL*Plus 与数据库的交互)