SELECT 基础
本教程共 50 篇 · 第 15 篇 · 更新于 2026-07-31
15. SELECT:查询基础
本节目标:学完本章你能用 SELECT 从表中取数据,掌握”查指定列""查全表""用 AS 起别名”,并真正理解什么是”结果集”。
开场:查询也是本地运算,没有服务器参与
和插入一样要绷紧一根弦:SQLite 是 serverless(无服务进程)/ 零配置 的。你敲一条 SELECT,SQLite 引擎直接打开那个 .db 文件、在内存里算一遍、把结果返回给你——全程没有独立的数据库服务在后台帮你算。这也意味着查询性能完全取决于你的 SQL 写得好不好、索引建没建,而不是”服务器够不够强”。
SELECT 是整个 SQL 里用得最频繁、也最核心的语句。本章只讲最基础的形状,后面的 WHERE、ORDER BY、JOIN 等都是在它之上叠加条件。把基础打牢,后面全是水到渠成。
结果集(Result Set)是什么
先建立一个最关键的认知:SELECT 不直接”给你看表”,它返回一个”结果集”。
结果集可以理解成:SQLite 按照你的指令,从表里挑出若干行、若干列,临时拼成的一张新表格。这张新表只存在于这次查询的返回里,并不会写回数据库文件(除非你用 INSERT INTO ... SELECT 主动存)。它有行有列、有表头,长得和原表一模一样,但它是”计算产物”,不是”原表本身”。
类比:原表是仓库里的一整本账册,SELECT 就像你跟会计说”把账册里所有姓张的客户、只抄姓名和电话给我”——会计重新抄了一份清单递给你,这份清单就是结果集,账册原件一字未动。
查全表:SELECT *
最省事的写法是 SELECT *,星号 * 是”所有列”的简写:
SELECT * FROM users;
命令行里实际效果(记得用 .headers on 和 .mode column 让输出更好看):
sqlite> .headers on
sqlite> .mode column
sqlite> SELECT * FROM users;
id name age email
-- ----- --- -------------------------
1 张三 28 zhangsan@example.com
2 李四 33 NULL
3 王五 41 wangwu@example.com
SELECT * 把 users 表的每一列、每一行都搬进结果集。
Warning生产代码里别用
SELECT *。 刚才材料里就强调过:*只在临时测试时图方便。一旦表结构变化(某列被删、新增一列),依赖*的程序可能拿到意料之外的列数,轻则逻辑错位、重则崩溃。开发时老老实实写出你真正要的列名,既是契约也是文档。
查指定列:只拿你需要的
绝大多数时候你不需要整行,只关心其中几列。把列名用逗号列出来即可:
SELECT name, email FROM users;
sqlite> SELECT name, email FROM users;
name email
----- -------------------------
张三 zhangsan@example.com
李四 NULL
王五 wangwu@example.com
为什么只查指定列更好? 三个理由:第一,返回数据量小,应用处理更快、内存更省;第二,列少了,结果集更聚焦,你一眼就能看到关心的信息;第三,也是最重要的,稳定——表加列不影响你的查询,表改列顺序也不影响。
何时用 *、何时用指定列? 简单粗暴的准则:在 CLI 里手动排查数据、临时看一眼,用 * 省事;写在程序里、要交付给别人或未来自己的,永远写指定列。
AS 别名:给列或表起个临时外号
查询结果里的列名默认就是原表的列名。但有时列名太长、或是你对列做了计算,想让结果集里的表头更友好,就用 AS 起别名:
SELECT name AS 姓名, age AS 年龄 FROM users;
sqlite> SELECT name AS 姓名, age AS 年龄 FROM users;
姓名 年龄
----- ---
张三 28
李四 33
王五 41
别名只作用于这次查询返回的结果集,不会改变原表结构,也不会写进数据库。它纯粹是为了”显示好看、语义清晰”。
除了给列起别名,AS 也能给表起别名,在多表连接(JOIN,后面章节会学)时特别有用,能大幅缩短语句:
SELECT u.name, o.amount
FROM users AS u
JOIN orders AS o ON u.id = o.user_id;
这里把 users 简称为 u、orders 简称为 o,后面引用列时写 u.name 就够了。
Tip别名里的
AS关键字其实可以省略,SELECT name 姓名和SELECT name AS 姓名等价。但初学者建议保留AS,可读性更强,一眼能看出”这是在起别名”。另外:如果别名里含空格或中文,需要用引号包起来,例如SELECT name AS "用户 姓名"。
结果集里列的顺序由你定
很多人以为”查出来的列顺序一定和表里一样”,这是误会。结果集里列的出现顺序,完全由你 SELECT 后列出的列名顺序决定,和原表物理顺序无关:
-- 把 age 放前面、name 放后面,结果集也跟着颠倒
SELECT age, name FROM users;
这在实际里很有用:你想让报表”先看到年龄再看姓名”,就不必去改表结构,查询时调一下列顺序即可。再次强调,结果集是临时拼出来的新表,它和原表是两回事——原表怎么存,不影响你怎么查;你怎么查,也不改原表。
DISTINCT 预览:结果去重
有时同一列里会有重复值,比如 orders 表里多个订单可能属于同一个 user_id。如果你只想知道”有哪些用户下过单”(不关心下了几单),可以用 DISTINCT 让结果集只保留不重复的组合:
SELECT DISTINCT user_id FROM orders;
DISTINCT 的完整用法(包括多列去重、和 GROUP BY 的区别)我们留到第 26 章细讲,这里你先知道 SELECT 有”去重”这个能力即可。
SELECT 还能直接计算:不查表也能用
SELECT 不只是”从表里取数”。它还能当计算器——不接任何表,直接算表达式:
SELECT 1 + 1;
SELECT 10 / 5, 2 * 4;
sqlite> SELECT 1 + 1;
2
sqlite> SELECT 10 / 5, 2 * 4;
2|8
这看起来像花拳绣腿,其实很有用:你可以用 SELECT 给每行做运算、拼接字符串、调用函数。比如给每个人的年龄加 1 岁展示(注意这只是结果集里展示,原表 age 不变):
SELECT name, age, age + 1 AS 明年年龄 FROM users;
NoteSELECT 后跟的表达式(Expression)会在结果集的每一行上各算一次。这里
age + 1是一行一行算的,返回的新列明年年龄也属于结果集,原表数据纹丝不动。表达式(Expression)、运算符(Operator)的概念,我们会在第 17 章系统讲。
执行顺序的小秘密
有个反直觉的点值得记住:SELECT 语句里,SELECT(挑列)写在最前面,但 SQLite 实际先执行 FROM(定位表),再执行各种过滤,最后才执行 SELECT(挑列)。所以语义上是”先确定从哪张表取,再决定留下哪些行,最后决定展示哪些列”。理解这个顺序,对你以后写复杂查询、排查”列找不到”的错误很有帮助。
类比小结
SELECT 就是”按指令抄清单”。SELECT * 是”整页照抄”——方便但潦草;SELECT 列名 是”只抄我圈出来的几栏”——精准且稳妥;AS 是”给抄出来的栏目标个更好懂的抬头”;而返回的整张清单,就是结果集:它是 SQLite 现场算出来的临时表格,原件(磁盘上的 .db 文件)一点没动。记住:查询不改数据,只产出数据;且因为 SQLite 没有服务器,这整笔账都是你本机文件 + 引擎现算的。