这一节的命令都很小,但天天要用。 核心只有一条分界线:它是发给数据库的,还是给这个工具自己的? 发给数据库的要分号(分号是「我说完了」的信号),给工具自己的不用,回车就执行。
2.5 节讲过 pragma_table_info 这类「能当表查」的字典视图,
这一篇的 DESCRIBE 就是它的等价物。
SPOOL 则和 3.2 节的 SET 配合使用:设好环境再存文件,文件才干净。
这一篇还有个隐性主题:怎么在不离开终端的情况下查东西、存东西、切出去再回来。远程排障时,
这三件事决定了你效率的高低。
一、先分清哪是 SQL、哪是命令
这是本节唯一需要背的东西。 判据一句话:这句话是要发给数据库执行的,还是给 SQL*Plus 这个程序自己执行的?
| 你敲的 | 属于谁 | 要不要分号 |
|---|---|---|
select * from emp; |
SQL | 要 |
alter table emp add (memo varchar2(50)); |
SQL | 要 |
desc emp |
SQL*Plus 命令 | 不要,回车就执行 |
spool out.txt |
SQL*Plus 命令 | 不要 |
help set |
SQL*Plus 命令 | 不要 |
show linesize |
SQL*Plus 命令 | 不要 |
为什么 SQL 要分号? 因为你可能一条语句敲三行,
数据库得知道你说完了没有。 分号就是这个信号。
SQL*Plus 命令都只有一行,不存在「还没说完」,所以不需要。
反过来说:一条 SQL 敲完忘了打分号,回车之后光标会跑到 2 等着你继续。
这时候别慌,补一个 ; 回车就执行了——不是卡死,是在等你把话说完。
二、HELP:查这个工具自己
HELP 查的不是 SQL 语法,是 SQL*Plus 这个工具怎么用。
HELP SET 列出 SET 的所有参数,HELP INDEX 列出所有能查的主题。
它是离线的。 没网、没文档、没手机,服务器上只有这个黑框时,HELP 是唯一能问的人。
内容不深,但参数名和默认值都在。
SHOW 是另一半:HELP 告诉你「有哪些」,SHOW 告诉你「现在是多少」。配了一堆参数之后,
SHOW ALL 能一次看全。排障时先 SHOW ALL,经常一眼就发现哪个参数被人改过。
三、DESCRIBE:看表结构
DESC emp 打出列名、类型、是否为空。接手一个陌生库,第一件事就是 desc。
它比 select * 强在哪? select * 给你数据,desc 给你结构:哪些列可以为空、
类型是什么、主键是谁。看结构才知道能不能插、该怎么查。
SQLite 里没有 desc,等价物是 pragma_table_info('emp')——而且它能当表查:
select name, type from pragma_table_info('emp')。
这一点 2.5 节讲过,这里再确认一次:字典能被 SQL 查,是它最有用的性质。
四、SPOOL:把终端内容存成文件
SPOOL 文件名 之后,屏幕上出现的所有东西同时写进文件;SPOOL OFF 才收尾落盘。
注意「所有东西」——包括你敲的那行命令和「已选择 N 行」。
所以不设 SET 就直接 spool,存下来的是一份「终端录像」,不是数据。
要干净的数据,先 SET PAGESIZE 0 FEEDBACK OFF HEADING OFF,再 spool。
最常见的坑:只 spool 不 off。 文件要么是空的,要么缺最后一段。
因为缓冲区还没刷出去。养成习惯:脚本最后一行永远写 SPOOL OFF。
还有一个更隐蔽的:spool 到一个已存在的文件会覆盖,不会追问。
路径写错、文件名写错,就把别人的东西盖了。 脚本里最好带上日期。
五、其他几个天天要用的
| 命令 | 干什么 |
|---|---|
/ |
重跑缓冲区里那条 SQL(不用重敲) |
@脚本.sql |
执行一个脚本文件;@@ 是「相对当前脚本的目录」 |
!命令 |
切到系统命令行执行一条,回来还在 SQL*Plus 里 |
ed |
把缓冲区内容丢进编辑器改,存盘退出后自动回填 |
clear screen |
清屏,输出太乱的时候用 |
exit/quit |
退出;是否提交由 SET EXITCOMMIT 决定 |
/ 是最省事的一条。 上一条 SQL 报错,改了条件之后敲一个 / 就重跑,
不用把整条再打一遍。 调 SQL 的时候这一条能省一半时间。
! 的价值在「不用退出」。 查个文件、看一眼磁盘、ping 一下,!df -h 回车,
回来还在原来的会话里。远程排障时,退出重连是有代价的。
exit 这一条要特别小心。 SET EXITCOMMIT 默认是 ON,
意思是退出时自动提交。 很多人以为「退出就等于放弃」,结果把测试数据留在了生产库上。
规矩:要放弃就直接 ROLLBACK 再退,别指望退出时不提交。
六、&变量:让脚本能带参数
脚本里写 &1、&deptno 这样的占位符,执行时 SQLPlus 会停下来问你取值。
这是命令行版的「参数化查询」。
好处是不用为每个部门改一遍脚本。 一个 report.sql,跑 @report.sql 10 就是部门 10 的报表,
跑 @report.sql 20 就是部门 20 的。
坑在字符串上: 如果 SQL 里写 where ename = '&name',输入 O'Brien 之后引号会打架。
正确写法是 where ename = q'[&name]',用 q 引号把整段括起来,里面的单引号就不算数了。
SET DEFINE OFF 会关掉这套替换。脚本里如果有 & 是普通字符(比如网址参数),
一定要先 SET DEFINE OFF,否则 SQLPlus 会停下来问你「&xxx 的值是多少」,脚本就卡住了。**
七、动手 Demo:DESCRIBE 与 SPOOL 的等价物
DESC 和 SPOOL 在 SQLite 里都没有对应命令,但它们干的事都能复现:DESC 是查结构,
pragma_table_info 能当表查;SPOOL 是录终端,就是「把本该上屏的东西写进文件」。
第二件事我特意演了「录进去的都是脏东西」这个后果——你会看到文件里混着提示符和「已选择 N 行」。
import sqlite3
c = sqlite3.connect(":memory:")
c.execute("CREATE TABLE dept (deptno INT, dname TEXT, loc TEXT)")
c.execute("CREATE TABLE emp (empno INT, ename TEXT, deptno INT)")
c.executemany("INSERT INTO dept VALUES (?,?,?)",
[(10, 'ACCOUNTING', 'NEW YORK'), (20, 'RESEARCH', 'DALLAS')])
c.executemany("INSERT INTO emp VALUES (?,?)",
[(7369, 20), (7566, 20), (7782, 10), (7839, 10), (7934, 10)])
# DESCRIBE 的等价物:pragma_table_info 能当表查
c.execute("select cid, name, type, \"notnull\", pk "
"from pragma_table_info('emp')").fetchall()
# SPOOL 的等价物:把「终端里本该出现的东西」写进文件
lines = ["SQL> select deptno, count(*) from emp group by deptno;", ""]
for d, n in c.execute("select deptno, count(*) from emp group by deptno"):
lines.append("%10d %10d" % (d, n))
lines += ["", "已选择 2 行。", "SQL> spool off"]
open("report.txt", "w", encoding="utf-8").write("\n".join(lines))
import os
print(os.path.getsize("report.txt"), "字节")
print(open("report.txt", encoding="utf-8").read())
# 行内代码里的 ** 会被当成加粗,这里只说结论:desc 不带分号,select 带
实跑输出:
--------------------------------------------------------------------
第十三课:常用 SQL*Plus 命令——HELP、DESCRIBE、SPOOL 和几个小开关
--------------------------------------------------------------------
**这一节的东西都很小,但天天要用。**
**重点是分清:哪些是 SQL,哪些是 SQL*Plus 自己的命令。**
先记住这条分界线
--------------------------------------------------------------------
SQL> select cmd, kind, semi from cmd
CMD KIND SEMI
-------------------------------------------------------------------------
`select * from emp;` SQL 要分号
`desc emp` SQL*Plus 命令 不要,回车就执行
`spool out.txt` SQL*Plus 命令 不要
`help set` SQL*Plus 命令 不要
`show linesize` SQL*Plus 命令 不要
`alter table emp add (memo varchar2(50));` SQL 要分号
已选择 6 行
**判据很简单:**
**它是「发给数据库的」还是「给这个工具自己的」?**
**发给数据库的要分号(分号是「我说完了」的信号),给工具自己的不用。**
DESCRIBE:看表结构
--------------------------------------------------------------------
[Oracle 原码 · 本沙箱不执行]
| DESCRIBE emp
| DESC emp
desc 是 describe 的缩写,两个都能用。
SQL> select cid as 序号, name as 列名, type as 类型, "notnull" as 非空, pk as 主键 from pragma_table_info('emp')
序号 列名 类型 非空 主键
-------------------------------
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 行
**SQLite 里没有 `desc`,等价物是 `pragma_table_info`。**
**注意它能当表查**——这一点 2.5 节说过,这里再确认一次。**
SPOOL:把输出存成文件
--------------------------------------------------------------------
**`SPOOL` 干的事:从这条命令开始,屏幕上出现的所有东西,同时写进文件。**
**`SPOOL OFF` 才真正落盘。**(很多人只 `spool` 不 `off`,文件是空的)
**写出来的文件 `report.txt`:**
| SQL> select deptno, count(*) as 人数 from emp group by deptno;
|
| DEPTNO 人数
| ---------- ----------
| 10 3
| 20 5
| 30 6
|
| 已选择 3 行。
| SQL> spool off
**大小 223 字节。**
**注意里面混着 `SQL>` 提示符和「已选择 N 行」**——
**这就是为什么上一节说:要给程序用,先把 `SET FEEDBACK OFF`。**
HELP 与 SHOW:查自己
--------------------------------------------------------------------
`HELP SET` **列出 SET 的所有参数**(不是讲 SQL 语法,是讲这个工具怎么配)。
`SHOW LINESIZE` **告诉你当前值是多少**。
**配了一大堆参数之后,`show` 是唯一的「我现在到底什么状态」。**
[Oracle 原码 · 本沙箱不执行]
| HELP SET
| SHOW LINESIZE
| SHOW PAGESIZE
| SHOW ALL
其他几个常用的
--------------------------------------------------------------------
SQL> select cmd, use from more
CMD USE
--------------------------------------------------------
`/` 重跑上一条 SQL(缓冲区里的那条)
`@脚本.sql` 执行一个脚本文件
`!命令` 切到系统命令行执行,回来还在 SQL*Plus 里
`ed` 把缓冲区的内容丢进编辑器改
`clear screen` 清屏,输出太乱的时候用
`exit`/`quit` 退出(注意:不 commit,未提交的就回滚了)
已选择 6 行
**最后一条要特别小心:**
**`exit` 会提交还是回滚,取决于你怎么配的**(`set exitcommit on|off`,默认是 `on`,
**会提交**)。**但很多人以为退出就等于放弃,结果把测试数据写进生产库了。**
**规矩:**
**要放弃就直接 `rollback` 再退,别指望「退出时不提交」。**
pragma_table_info('emp') 打出 8 行、5 列:序号、列名、类型、是否非空、是否主键。
desc 在 SQL*Plus 里排版更好看,信息量是一样的。
注意 notnull 那一列我加了双引号。 因为它是 SQLite 的关键字,不加引号会被当成语法的一部分。
这类「名字撞关键字」的坑,换引擎时经常遇到。
录出来的文件里,第一行是 SQL> select ...,最后一行是 SQL> spool off,
中间还夹着一句「已选择 2 行」。这三样都不是数据。
这就是为什么要先 SET FEEDBACK OFF:
spool 忠实记录「屏幕上的一切」,它不负责帮你挑出数据。
挑不挑得干净,取决于你 spool 之前设了什么。
文件大小也说明问题:两条数据占不了几个字节,剩下全是这些包装。数据量小的时候看不出来,
上万行的时候,这些包装会让文件大出一截,解析也更容易出错。
30 秒实验
- 把
report.txt里那行SQL> select ...和「已选择 2 行」手动删掉,再读一次。 这就是「干净的 spool 文件」长什么样——只剩数据,程序可以直接按空白切。 - 用
pragma_table_info('dept')再查一次,对比两张表的列数和主键。 你会发现emp没有主键(我建表时没写),所以pk那一列全是 0。 - 写一个小脚本,把
@脚本.sql和!两种用法各演一次。 注意!之后的命令是给操作系统执行的,@之后的文件是给 SQL*Plus 执行的—— 两个符号长得像,归属完全不同的层。
常见误区
- 给 SQL*Plus 命令加分号。
desc emp;不算错, 但spool out.txt;会把分号当成文件名的一部分。命令不带分号,是这套工具的基本约定。 spool之后不off。 文件空的,或者缺最后一段。 缓冲区没刷出去,spool就还没真正结束。- 以为
exit等于放弃修改。SET EXITCOMMIT默认ON,退出时自动提交。 要放弃必须先ROLLBACK。 - 脚本里出现
&却没SET DEFINE OFF。 SQLPlus 会停下来问你要值, 定时任务里没人回答,脚本就永远卡在那。*
自测三问(点开对答案)
1. 怎么一眼看出你敲的是 SQL 还是 SQL*Plus 命令? —— 问一句: **这句话是发给数据库的,还是给这个工具自己的?**发给数据库的要分号(表示「我说完了」), 给工具自己的不用。 2. `SPOOL` 出来的文件里为什么会有「已选择 N 行」? —— 因为 `spool` 录的是**屏幕上的一切**,不是查询结果。 **要干净数据,`spool` 之前先 `SET FEEDBACK OFF HEADING OFF PAGESIZE 0`。** 3. `DESC` 和 `SELECT *` 的区别在哪? —— `DESC` 给**结构**(列名、类型、是否可空、主键),`SELECT *` 给**数据**。 **接手陌生表先 `desc`**,看结构才知道能怎么查、能不能插。下一篇讲把查询结果排成报表:
COLUMN 管列,TTITLE/BTITLE 管页。这一手在只有终端的服务器上特别值钱。
- ☐ (本篇完,下一篇 3.4 格式化查询结果)
相关笔记
- 设置 SQL*Plus 的运行环境
- 数据字典
-
- 🦏 MySQL即学即用/00-索引
-
- 🗄️ 数据库基础/00-索引
- 🏺 Oracle从入门到精通 · 索引