DESCRIBE、DESC、SHOW COLUMNS FROM 是同一条语句,给六列。
它看不到引擎和字符集,那两样只在 SHOW CREATE TABLE 里有。
接手一个库的第一个动作:这三句连着敲,别直接 SELECT *。
6.1 建完表,这一篇讲怎么验收——建出来的东西是不是你想的那样。 它是第 4 章「查看数据库」的下一层:那篇看库,这篇看表。 这一篇的三句查法,在第 6.3 节改结构前后都要各跑一次。
一、DESCRIBE:看列的六件事
三个名字一条语句,随便用哪个都一样。
DESCRIBE tb_member;
DESC tb_member;
SHOW COLUMNS FROM tb_member;
它给六列,教材 6.2 逐列讲过,这里按「最常看」排序:
| 列 | 看什么 | 常用来判断 |
|---|---|---|
| Null | 能不能为空 | NO 表示必须给值 |
| Default | 默认值 | 插数据时可以省略这一列 |
| Key | 是不是索引 | PRI 主键、UNI 唯一、MUL 普通 |
| Extra | 额外属性 | auto_increment 在这一列 |
| Type | 类型 | 是不是选窄了 |
| Field | 列名 | 拼 SQL 时别写错 |
第二列有一个坑:Default 显示 NULL 有两种含义。
一种是「默认值就是 NULL」,另一种是「没定默认值」——在这里长得一模一样。
要分清得去看 SHOW CREATE TABLE。
第三列也有盲区:它只说「有索引」,不说索引叫什么、几列、是不是唯一。
这些要看 SHOW INDEX。
二、SHOW CREATE TABLE:唯一能看到引擎的地方
这句话给的是「重新建一张一模一样的表」所需的完整语句。
所以它必然包含 DESCRIBE 看不到的东西:引擎、字符集、排序规则、外键约束名。
SHOW CREATE TABLE tb_member\G
末尾那个 \G 是把结果竖着打。
不加它,一条长语句会被终端折成难读的几行;加上它,一列一行,好读得多。
这句话的三个用途,从轻到重:
一是想知道引擎和字符集;二是想照抄一张表的结构;三是备份建表语句。
第三个用途最实在。
数据可以用 mysqldump 备,但「这张表本来长什么样」只有这一句能给全。
接手老系统时先把所有表的 SHOW CREATE TABLE 存一份,比存数据有用。
三、索引和外键要另查
表结构是三样东西:列、索引、约束。三样在不同的地方。
| 要看什么 | 用哪一句 |
|---|---|
| 列定义 | DESCRIBE 表 |
| 完整建表语句(含引擎、字符集) | SHOW CREATE TABLE 表 |
| 索引明细 | SHOW INDEX FROM 表 |
| 外键 | 查 information_schema.KEY_COLUMN_USAGE |
| 表的行数、大小、引擎 | SHOW TABLE STATUS LIKE '表' |
这五句凑齐,才算把一张表看全了。
很多人只跑第一句,然后在「为什么这条查询不走索引」上卡半天——因为索引根本没看。
第五行是运营视角:Rows 那一列是估算值,不是精确行数。
InnoDB 的 Rows 是从统计信息里读的,误差可能到几成,别拿它当准确数。
四、接手一个库,先跑这三句
教材 6.2 讲完语法就结束了,实际工作里这三句是连着用的。
SHOW TABLES; -- 有哪些表
SHOW CREATE TABLE 表名; -- 这张表长什么样
SHOW INDEX FROM 表名; -- 索引有哪些
第一句给名单,第二句给结构,第三句给性能线索。
三句都跑完,你对这个库的了解就超过「能查出数据」的层次了。
为什么强调「别直接 SELECT *」?
因为它只给你数据,不给你结构。
而线上事故八成出在结构上:类型选窄了、索引少了、字符集不对。
还有一个更实用的理由:SELECT * 在大表上会把整行拉出来,包括那些 TEXT 大字段。
查结构不产生任何数据读取,是安全的操作。
五、动手 Demo:三种看法,信息量差很多
这一课把三种查法摆在一起:DESCRIBE、SHOW CREATE TABLE、SHOW INDEX。
沙箱里对应的是 PRAGMA table_info、sqlite_master、PRAGMA index_list。
看完你会知道每种查法各漏了什么。
# 一、列定义:六列,和 DESCRIBE 一一对应
for cid, name, typ, notnull, dflt, pk in c.execute("PRAGMA table_info(tb_member)"):
print(name, typ, notnull, dflt, pk)
# 二、完整建表语句:只有这里能看到 CHECK 和 UNIQUE 的原文
print(c.execute("SELECT sql FROM sqlite_master WHERE name = 'tb_member'").fetchone()[0])
# 三、索引:DESCRIBE 的 Key 列看不出这些
for seq, name, uniq, origin, partial in c.execute("PRAGMA index_list(tb_member)"):
print(name, uniq, origin)
实跑输出:
--------------------------------------------------------------------
第二十六课:三种看法,信息量差很多
--------------------------------------------------------------------
**教材 6.2 讲 `DESCRIBE`。它好用,但它看不到引擎和字符集。**
**要全的信息得用 `SHOW CREATE TABLE`。**
**这一课把三种查法摆在一起,看它们各自能看到什么、看不到什么。**
一、DESCRIBE:看列的六件事
--------------------------------------------------------------------
**`DESCRIBE`、`DESC`、`SHOW COLUMNS FROM` 三个是同一条语句的三种写法。**
**它给六列:字段名、类型、能不能为空、是不是键、默认值、额外信息。**
字段 类型 空 键 默认 额外
-------------------------------------------------------------
id INTEGER YES PRI NULL auto_increment
account TEXT NO NULL
nick TEXT NO ''
age INTEGER YES NULL
score REAL YES 0
joined TEXT YES CURRENT_TIMESTAMP
**六列里最常看的是第三列和第五列。**
**第三列是「能不能为空」,`NO` 表示插数据时必须给值(或者有默认值)。**
**第一行那格是 `YES`,这一点跟 MySQL 不一样,得说明一下。**
**SQLite 的 `INTEGER PRIMARY KEY` 并不拦 NULL,插 NULL 会自动填自增值;MySQL 的主键列必然是 `NO`。**
**所以这一格别当结论,当「两边实现不同」的例子看。**
**第五列那个 `NULL` 有两种含义,要分清。**
**一种是「默认值就是 NULL」,另一种是「没定默认值」——`DESCRIBE` 里这两者长得一样。**
**要分清得去看 `SHOW CREATE TABLE`。**
二、SHOW CREATE TABLE:唯一能看到引擎和字符集的地方
--------------------------------------------------------------------
**沙箱里这张表定义存在 `sqlite_master` 里,MySQL 里用 `SHOW CREATE TABLE` 取。**
**这是它给出来的原文(我把长行折了一下,逗号留在原处):**
`CREATE TABLE tb_member (id INTEGER PRIMARY KEY AUTOINCREMENT,`
`account TEXT NOT NULL UNIQUE,`
`nick TEXT NOT NULL DEFAULT '',`
`age INTEGER CHECK (age BETWEEN 1 AND 150),`
`score REAL DEFAULT 0,`
`joined TEXT DEFAULT CURRENT_TIMESTAMP)`
**这里能看到的、`DESCRIBE` 看不到的东西有三样。**
**一是自增属性挂在哪一列上;二是唯一约束;三是 CHECK 约束的完整条件。**
**真 MySQL 的输出还会多一样最关键的:末尾那行 `ENGINE=InnoDB DEFAULT CHARSET=utf8mb4`。**
**所以「这张表到底用的什么引擎」只有一个可靠答案——`SHOW CREATE TABLE`。**
**`DESCRIBE` 看不出来,`SELECT *` 更看不出来。**
三、索引和外键要另查
--------------------------------------------------------------------
索引名 列 唯一 来路
-------------------------------------------------------
i_nick nick 否 自建
sqlite_autoindex_tb_member_1 account 是 约束自动建
**`DESCRIBE` 的输出里,「键」那一列只有 `PRI`、`UNI`、`MUL` 三个值。**
**它告诉你「有索引」,但不告诉你索引叫什么、几列、是不是唯一。**
**所以查表结构的正确姿势是三句连着敲:**
[教材原码 · 本沙箱不执行]
| DESCRIBE tb_member; -- 列定义
| SHOW CREATE TABLE tb_member; -- 完整建表语句(引擎、字符集都在这)
| SHOW INDEX FROM tb_member; -- 索引明细
② 教材原码,本沙箱不执行
**三句各有各的盲区,凑齐才是一张表的样子。**
**接手的第一个动作就应该是这三句,而不是直接 `SELECT *`。**
第一段那张六列表就是 DESCRIBE 的样子。
注意 id 那一行的「空」列给的是 YES,这一点跟 MySQL 不一样。
SQLite 的 INTEGER PRIMARY KEY 并不拦 NULL,插 NULL 会自动填自增值;
MySQL 的主键列必然是 NO。
第二段把建表语句原文打出来了。
DESCRIBE 看不到的三样东西都在这里:自增属性挂在哪列、唯一约束、CHECK 的完整条件。
真 MySQL 还会多一样最关键的:末尾那行 ENGINE=InnoDB DEFAULT CHARSET=utf8mb4。
第三段那张索引表有个细节:sqlite_autoindex_tb_member_1。
它是 UNIQUE 约束自动建的索引,名字是系统起的。
所以「这条查询为什么走了个我没建过的索引」,答案往往是某个约束替你建的。
三张表看完可以下一个结论:DESCRIBE 适合快速看列,要全的信息只能靠后两种。
30 秒实验
- 给表加一列
remark TEXT,再跑一次DESCRIBE,看新列在最后一行。 新列总在最后,因为ADD COLUMN只能往末尾加。 想插在中间只能重建表。 - 把
nick列的默认值从''改成'匿名',看DESCRIBE的Default列怎么变。 跟着变。这一列就是「不填时补什么」的直接体现。 - 跑一次
SHOW TABLE STATUS LIKE 'tb_member',看Rows和实际行数差多少。 大概率不准。InnoDB 的Rows是统计信息估算值,不是COUNT(*)。
常见误区
- 「
DESCRIBE能看到表的一切。」 看不到引擎、字符集、排序规则、外键、索引明细。 这五样恰恰是排查问题时最常要看的。 - 「
SHOW TABLE STATUS的Rows就是行数。」 是估算值。 要准确数只能用COUNT(*),代价是全表或全索引扫一遍。 - 「建表语句太长,用
SELECT *看一列名字就行。」SELECT *在空表上什么都看不到,还会拉全部大字段。 看结构就用看结构的语句。 - 「
SHOW CREATE TABLE的输出可以原样再执行一遍。」 表已存在时会报错,要先把CREATE TABLE改成CREATE TABLE IF NOT EXISTS,或者换个表名。 而且它带AUTO_INCREMENT=当前值,抄过去会连计数器一起抄。
自测三问(点开对答案)
1. 想知道一张表用的什么引擎,用哪一句? —— **`SHOW CREATE TABLE 表名`,末尾那行写着 `ENGINE=...`。** **`DESCRIBE` 看不到,`SHOW TABLE STATUS` 也能看到但信息更杂。** 2. `DESCRIBE` 的 `Key` 列有哪三个值? —— **`PRI` 主键、`UNI` 唯一索引的第一列、`MUL` 普通索引的第一列。** **它只说「有」,不说索引名和列数。** 3. 为什么接手一个库要先跑 `SHOW CREATE TABLE`? —— **因为它是唯一能给全结构的一句,包含引擎、字符集、约束名。** **而且它可以直接当建表脚本存档,比存数据更能还原现场。**6.2 到这儿结束。下一篇 6.3 讲改表结构——建表时想漏的东西,后面都得靠 ALTER TABLE 补。
- ☐ (本篇完,下一篇 6.3 修改表结构)