牛客 SQL2:查询多列要求获取设备 ID 对应的性别、年龄和学校。答案就是把需要的四列列出来:
SELECT device_id, gender, age, university FROM user_profile;我原稿只写了“查询多列,不是 * 所有列”。这个判断对,但还可以进一步看:列清单决定结果的形状,WHERE 决定保留哪些行,DISTINCT 决定是否合并重复结果。三个动作不能混为一谈。
1. 取四列,并不是取四行
表:id | device_id | gender | age | university | province | | | | 结果: device_id | gender | age | university这里保留所有输入行对应的结果,只选指定字段。没有 WHERE 就不因年龄未知而排除用户;没有 DISTINCT 就不会自动合并相同学校或相同结果行。
列的顺序也由 SELECT 清单决定,而不是必须与建表顺序一致。如果接口按列位置读取,改动清单顺序可能改变程序的解释,不能只看列值有没有出现。
2. 建一个包含 NULL 和重复结果的小例子
以下独立表用于自己的练习数据库,表名已存在时换一个名字。
CREATE TABLE sql2_projection_demo ( id INT NOT NULL PRIMARY KEY, device_id INT NOT NULL, gender VARCHAR(14) NOT NULL, age INT, university VARCHAR(32) NOT NULL, province VARCHAR(32) NOT NULL ); INSERT INTO sql2_projection_demo VALUES (1,2138,'male',21,'北京大学','北京'), (2,2138,'male',21,'北京大学','其他'), (3,3214,'male',NULL,'复旦大学','上海');这里是构造的反例,不是原题表的完整复制。第 1、2 行有不同 id 和 province,但选出的四列完全相同。
SELECT device_id, gender, age, university FROM sql2_projection_demo ORDER BY id;| device_id | gender | age | university |
|---|---|---|---|
| 2138 | male | 21 | 北京大学 |
| 2138 | male | 21 | 北京大学 |
| 3214 | male | NULL | 复旦大学 |
它还是三行。年龄为 NULL 的用户没有消失,两条看起来相同的结果也都在。如果需求只是原题的数据读取,不能为了结果“更好看”随手加 DISTINCT 或 WHERE age IS NOT NULL。
3. 不同需求,要显式写不同操作
如果需求变成只保留年龄已知的用户:
SELECT device_id, gender, age, university FROM sql2_projection_demo WHERE age IS NOT NULL ORDER BY id;返回两行,但两行仍然相同。筛行不是去重。
如果需求变成合并完全相同的四列结果:
SELECT DISTINCT device_id, gender, age, university FROM sql2_projection_demo;结果集合有两项,其中一项仍有 NULL。这与按 user id 留一条用户记录也不是同一件事。去重的专门对照。无 ORDER BY 的这条查询不承诺固定结果顺序。
4. 新增一列以后,SELECT *会发生什么?
先查看本实验表的所有列:
SELECT * FROM sql2_projection_demo ORDER BY id;结果有六列。现在只在这个独立练习表上增加字段,不要把下面 DDL 随便复制到业务表:
ALTER TABLE sql2_projection_demo ADD COLUMN extra_note VARCHAR(32) DEFAULT 'new';再跑相同的 SELECT *,结果变成七列;再跑第 2 节的显式四列查询,列名、列顺序和行值都不变。
| 写法 | 新增字段前 | 新增字段后 |
|---|---|---|
| SELECT * | 6 列 | 7 列 |
| 明确写四列 | 4 列 | 仍是原来的 4 列 |
这就是显式列清单的一个实际收益:不相关字段变动,不会自动扩大本次结果。它不是对所有结构变化的免疫,删除或改名被引用的列仍可能让查询失败。
5. 少取几列一定更快吗?
不一定。少传无用字段可能减少结果传输与应用解析,也可能让某些查询有机会使用覆盖索引;但有没有收益取决于索引、数据量、执行计划和访问方式。选少几列不等于存储引擎只读取对应字段的几个字节,更不等于必然少读磁盘页。
这里没有跑 MySQL EXPLAIN 或性能基准,因此不把列清单实验写成“速度提升多少”的结论。MySQL SELECT 语法。
6. 我怎么核对结果形状
本次在 SQLite 内存数据库实际运行正文实验,检查四列的名字和顺序、每一行的值、重复结果和 NULL 是否保留,以及 ALTER 前后的两种输出。也检查空表与单行表。
返回 0 行时,只检查行数组还不够,无法知道结果列是什么。测试器同时读取查询的列元数据:空表上的显式查询仍有那四列。MySQL 语法按文档核对;这不是 MySQL 存储引擎或性能测试。
这道题虽简单,却能练一个很实用的习惯:先确认要返回哪些列、哪些行、是否去重、是否有顺序要求。把结果形状说清楚,往往比把 SELECT * 换成一长串字段名更重要。