SET 系列命令只改一件事:
输出长什么样。一行数据都不动。但它决定了你在终端里能不能把结果看清楚。
最常改的四个:
LINESIZE 一行多宽、PAGESIZE 一页几行、FEEDBACK 要不要报行数、HEADING 要不要表头。
上一篇讲的是「话怎么递过去」,这一篇讲「回来的东西怎么摆」。
两件事完全无关:换一个客户端,这些设置全都不认,但数据一个字节不差。
这里的每个开关,后面写脚本、导数据时都会再用到。
尤其是 PAGESIZE 0 这一条,不知道它的人经常花半天去解析「已选择 N 行」。
一、SET 改的全是「皮」
先把边界划清楚:SET 是 SQL*Plus 这个客户端自己的命令,不是 SQL。它不发给数据库,
数据库根本不知道你设了什么。
所以它不带分号(带了也不算错,但没必要),而且一退出就没了——下次进来又是默认值。
要长期生效,得写进 login.sql 或 glogin.sql。
这四个默认值先记住:
LINESIZE 默认 80,PAGESIZE 默认 14,FEEDBACK 默认 ON,HEADING 默认 ON。
80 列是上古终端的宽度,拿到今天的数据上,一定不够。
二、LINESIZE:一行多宽
SET LINESIZE 200 之后,一行最多放 200 个字符。
超了不会横向滚动,是直接折到下一行——这是最容易误判的地方:你以为表被切碎了,其实是折行了。
默认 80 遇上六列表格,最后一列必然被甩到下一行去。更糟的是折行之后表头和数据行错位,
看着像两列数字对不上。
上限是 32767(老版本 2000)。写脚本时直接拉满,不用心疼——反正只在输出时起作用,不占内存。
三、PAGESIZE:一页几行
PAGESIZE 是「一屏多少行」,每翻一页 SQL*Plus 都会重新打一遍表头。
人在终端里看,这个设计是对的:翻过去还能知道每列是什么。
但输出到文件时它就是垃圾。 一份一万行的数据,文件里会夹着几百行重复表头和一串空行。
所以导数据第一件事:SET PAGESIZE 0。
PAGESIZE 0 的意思是「不分页」,不是「一页零行」。
这类「0 表示关闭」的约定在 SQL*Plus 里很常见,别按字面理解。
四、FEEDBACK 与 HEADING:要不要那些话
已选择 14 行。 这句话是 FEEDBACK 打的,不是查询结果的一部分。
SET FEEDBACK OFF 之后,这条提示消失,但数据一行不少。
SET HEADING OFF 去掉表头。两个一起关,输出就变成纯数据——这正是程序想要的样子。
但有个坑:FEEDBACK 关掉之后,你就不知道一条 update 改了几行。
交互式排障时不要关它,update 打出「已更新 0 行」往往就是答案。
同一个开关,导数据时要关,查问题时必须开。
五、给程序吃的数据,一套固定模板
记住这一行,能省很多事:
SET PAGESIZE 0 FEEDBACK OFF HEADING OFF LINESIZE 32767 TRIMSPOOL ON
TRIMSPOOL ON 去掉行尾空格,否则每个字段后面都是一串空格,split 出来一堆空值。
这一条教材没细讲,但不加会很难受。
再加一句 SET TERMOUT OFF(只写文件不上屏),导十万行的时候屏幕就不会卡死。
TERMOUT 是「终端输出」的总闸,SPOOL 照样工作。
六、还有几个一设就见效的
SET TIMING ON:每条 SQL 后面打出耗时。调优第一步不是看执行计划,是先知道哪条慢。
SET SERVEROUTPUT ON:不开这个,dbms_output.put_line 打出来的东西你一个字都看不到。
这是新手最常见的「明明执行了却没输出」。
SET AUTOTRACE ON:附上执行计划和统计信息。
要先用 plustrce.sql 建 plustrace 角色,否则报权限错。
它和 TIMING 一起开,是命令行调优的标准姿势。
七、动手 Demo:同一份数据,四种渲染
我写了个小函数,按 LINESIZE 真的把行折断、按 PAGESIZE 真的分页。
数据完全不动,只改渲染参数,你看输出差多少。
四组对照:linesize 40 折成半截、linesize 120 完整、pagesize 5 分页、feedback 与 heading 全关。
import sqlite3
c = sqlite3.connect(":memory:")
c.execute("CREATE TABLE emp (empno INT, ename TEXT, job TEXT, "
"hiredate TEXT, sal REAL, deptno INT)")
c.executemany("INSERT INTO emp VALUES (?,?,?,?,?,?)", [
(7369, 'SMITH', 'CLERK', '1980-12-17', 800.0, 20),
(7499, 'ALLEN', 'SALESMAN', '1981-02-20', 1600.0, 30),
(7566, 'JONES', 'MANAGER', '1981-04-02', 2975.0, 20),
(7839, 'KING', 'PRESIDENT', '1981-11-17', 5000.0, 10),
(7934, 'MILLER', 'CLERK', '1982-01-23', 1300.0, 10),
])
rows = c.execute("select empno, ename, job, hiredate, sal, deptno "
"from emp order by empno").fetchall()
def render(rows, linesize, feedback=True, heading=True):
"""按 linesize 折断,超宽就截断并打 ~ 标记。"""
cols = ["EMPNO", "ENAME", "JOB", "HIREDATE", "SAL", "DEPTNO"]
widths = []
for i, name in enumerate(cols):
w = max([len(name)] + [len(str(r[i])) for r in rows]) + 1
widths.append(w)
def fit(line):
if len(line) <= linesize:
return [line]
return [line[:linesize - 1] + "~"]
out = []
if heading:
out += fit("".join(n.ljust(w) for n, w in zip(cols, widths)).rstrip())
out.append("-" * linesize)
for r in rows:
out += fit("".join(str(v).ljust(w)
for v, w in zip(r, widths)).rstrip())
if feedback:
out += ["", "已选择 %d 行。" % len(rows)]
return out
for ln in render(rows, 40): print(ln) # 太窄,最后一列没了
print('=' * 50)
for ln in render(rows, 120): print(ln) # 够宽,六列都在
print('=' * 50)
for ln in render(rows, 120, feedback=False, heading=False):
print(ln) # 给程序吃的干净数据
实跑输出:
--------------------------------------------------------------------
第十二课:设置 SQL*Plus 的运行环境——SET 命令改的全是「皮」
--------------------------------------------------------------------
**`SET` 系列命令只改「输出长什么样」,一行数据都不动。**
**但它决定了你在终端里能不能看清结果。**
**教材里最常见的那几个:** `LINESIZE`(一行多宽)、`PAGESIZE`(一页几行)、
`FEEDBACK`(要不要打「已选择 N 行」)、`HEADING`(要不要表头)。
先把「一行多宽」这件事演出来
--------------------------------------------------------------------
[Oracle 原码 · 本沙箱不执行]
| SET LINESIZE 200
| SET PAGESIZE 50
| SET FEEDBACK ON
| SET HEADING ON
**`LINESIZE` 太小,一行会被折断**——**SQL*Plus 不是横向滚动,是直接换行,看起来就像表被切碎了。**
linesize 40:宽表被折成两截
--------------------------------------------------------------------
EMPNO ENAME JOB HIREDATE SAL ~
----------------------------------------
7369 SMITH CLERK 1980-12-17 800.0~
7499 ALLEN SALESMAN 1981-02-20 1600.~
7521 WARD SALESMAN 1981-02-22 1250.~
7566 JONES MANAGER 1981-04-02 2975.~
7654 MARTIN SALESMAN 1981-09-28 1250.~
7698 BLAKE MANAGER 1981-05-01 2850.~
7782 CLARK MANAGER 1981-06-09 2450.~
7788 SCOTT ANALYST 1987-04-19 3000.~
7839 KING PRESIDENT 1981-11-17 5000.~
7844 TURNER SALESMAN 1981-09-08 1500.~
7876 ADAMS CLERK 1987-05-23 1100.~
7900 JAMES CLERK 1981-12-03 950.0~
7902 FORD ANALYST 1981-12-03 3000.~
7934 MILLER CLERK 1982-01-23 1300.~
已选择 14 行。
**行尾那个 `~` 是截断标记**(真 SQL*Plus 不标,直接换行,更难读)。
**看出问题了吗:最后一列 `DEPTNO` 整列没了,`SAL` 顶到了本该属于它的位置上;`1300.0` 还被砍成 `1300.`。**
**一张六列的表,看上去像五列——而且没人告诉你少了一列。**
linesize 120:一列都不折
--------------------------------------------------------------------
EMPNO ENAME JOB HIREDATE SAL DEPTNO
------------------------------------------------
7369 SMITH CLERK 1980-12-17 800.0 20
7499 ALLEN SALESMAN 1981-02-20 1600.0 30
7521 WARD SALESMAN 1981-02-22 1250.0 30
7566 JONES MANAGER 1981-04-02 2975.0 20
7654 MARTIN SALESMAN 1981-09-28 1250.0 30
7698 BLAKE MANAGER 1981-05-01 2850.0 30
7782 CLARK MANAGER 1981-06-09 2450.0 10
7788 SCOTT ANALYST 1987-04-19 3000.0 20
7839 KING PRESIDENT 1981-11-17 5000.0 10
7844 TURNER SALESMAN 1981-09-08 1500.0 30
7876 ADAMS CLERK 1987-05-23 1100.0 20
7900 JAMES CLERK 1981-12-03 950.0 30
7902 FORD ANALYST 1981-12-03 3000.0 20
7934 MILLER CLERK 1982-01-23 1300.0 10
已选择 14 行。
**同一份数据,只是把 `LINESIZE` 从 40 改到 120。**
**这就是「环境设置」的全部意义:它不改数据,只决定你能不能看清。**
pagesize 5:分成三页,每页都重复表头
--------------------------------------------------------------------
EMPNO ENAME JOB HIREDATE SAL DEPTNO
------------------------------------------------
7369 SMITH CLERK 1980-12-17 800.0 20
7499 ALLEN SALESMAN 1981-02-20 1600.0 30
7521 WARD SALESMAN 1981-02-22 1250.0 30
7566 JONES MANAGER 1981-04-02 2975.0 20
7654 MARTIN SALESMAN 1981-09-28 1250.0 30
---- 第 2 页 ----
EMPNO ENAME JOB HIREDATE SAL DEPTNO
------------------------------------------------
7698 BLAKE MANAGER 1981-05-01 2850.0 30
7782 CLARK MANAGER 1981-06-09 2450.0 10
7788 SCOTT ANALYST 1987-04-19 3000.0 20
7839 KING PRESIDENT 1981-11-17 5000.0 10
7844 TURNER SALESMAN 1981-09-08 1500.0 30
---- 第 3 页 ----
EMPNO ENAME JOB HIREDATE SAL DEPTNO
------------------------------------------------
7876 ADAMS CLERK 1987-05-23 1100.0 20
7900 JAMES CLERK 1981-12-03 950.0 30
7902 FORD ANALYST 1981-12-03 3000.0 20
7934 MILLER CLERK 1982-01-23 1300.0 10
已选择 14 行。
**`PAGESIZE` 是「一屏几行」**,
翻页时 SQL*Plus 会**重新打表头**——
**所以你会看到表头反复出现。**
**输出到文件时,很多人忘了设 `PAGESIZE 0`,结果文件里全是重复表头和空行。**
FEEDBACK 与 HEADING
--------------------------------------------------------------------
7369 SMITH CLERK 1980-12-17 800.0 20
7499 ALLEN SALESMAN 1981-02-20 1600.0 30
7521 WARD SALESMAN 1981-02-22 1250.0 30
**`SET FEEDBACK OFF` 去掉「已选择 N 行」,`SET HEADING OFF` 去掉表头。**
**这两个开关在「生成数据文件」时特别有用**——**你要的是干净的数据,不是一张给人看的表。**
**一条经验:任何要交给程序处理的数据,一律 `SET PAGESIZE 0 FEEDBACK OFF HEADING OFF LINESIZE 32767`。** **否则你会花半天去解析那些「已选择 N 行」。**
linesize 40 那一组,最后一列 DEPTNO 整列没了,SAL 顶到了本该属于它的位置上,
1300.0 还被砍成 1300.。
一张六列的表看上去像五列,而且没有任何提示。这就是默认 80 在宽表上的真实后果。
行尾那个 ~ 是我加的截断标记。真 SQL*Plus 不标,直接折行——所以真环境里更难发现少了东西。
它给不了你这个提示,只能你自己数列。
linesize 120 那一组什么都没变,只是宽了。
同一份数据、同一条 SQL,唯一区别是渲染参数——这就是「环境设置不改数据」最直白的证据。
pagesize 5 那一组,表头出现了三次。
这是分页的正确行为(人翻屏要看表头),但对文件就是灾难。
导数据不设 PAGESIZE 0,文件里就会长这样。
最后一组把 FEEDBACK 和 HEADING 都关了,输出只剩数据行。
注意列之间还是对齐的(空格还在),只是没有表头告诉你每列是什么。
这就是程序想要的:它不需要知道列名,它按位置取。
30 秒实验
- 把 linesize 改成 60 再跑一次,找到「刚好不折」的那个临界宽度。 做法是把六列的实际宽度加起来:六列的宽度加一之和,就是临界值。
- 给
render加一个pagesize=0的分支,确认输出里没有---- 第 N 页 ----。PAGESIZE 0是关闭分页,不是「一页零行」——这个坑要亲手踩一次才记得住。 - 把
feedback=False改成只关HEADING、保留FEEDBACK,看输出变成什么样。 这时候你能知道有几行,但不知道每列是什么。
常见误区
- 以为
SET LINESIZE能改变数据。 它只影响显示。 你把LINESIZE设成 10,数据一个字节没变,只是每条都折成一堆碎片。 - 以为
SET设完就永久生效。 它是会话级的,退出就没了。 要长期生效得写进login.sql,或者干脆写在脚本开头。 - 导数据时不设
PAGESIZE 0。 结果文件里全是重复表头和「已选择 N 行」, 再用脚本去解析,就得写一堆过滤逻辑——本来一行设置能解决的事。 - 把
SET SERVEROUTPUT ON忘了。 PL/SQL 里dbms_output.put_line打了半天, 屏幕上一个字没有,不是没执行,是没开这个开关。
自测三问(点开对答案)
1. `SET LINESIZE 40` 之后,表看起来少了一列,数据丢了吗? —— 没丢。**折行或截断只发生在显示层**,`select` 出来的还是那么多列。 **换一个宽点的终端,或者把 `LINESIZE` 调大,列就回来了。** 2. 导数据给程序用,必须设哪几个参数? —— **`PAGESIZE 0`(关分页)、`FEEDBACK OFF`(关行数提示)、`HEADING OFF`(关表头)、** **`LINESIZE 32767`(放宽)、`TRIMSPOOL ON`(去行尾空格)。**数据量大再加 `TERMOUT OFF`。 3. 为什么交互排障时不能关 `FEEDBACK`? —— 因为它会告诉你「已更新几行」。 **`update` 打出「已更新 0 行」,往往直接说明条件写错了**——关掉它,这个最重要的线索就没了。下一篇讲常用命令:
HELP、DESCRIBE、SPOOL,还有 /、@、! 这几个小开关。重点是分清哪些是 SQL、
哪些是工具自己的命令。
- ☐ (本篇完,下一篇 3.3 常用 SQL*Plus 命令)
相关笔记
- SQL*Plus 与数据库的交互
- 数据字典
-
- 🐬 MySQL必知必会/00-索引
-
- 🦏 MySQL即学即用/00-索引
- 🏺 Oracle从入门到精通 · 索引