DQL(Data Query Language,数据查询语言)是 SQL 中最核心、使用频率最高的部分。它的唯一使命就是从数据库中检索数据。虽然 SQL 标准中将其归类为 DML 的一部分,但在实际工程和教学中,由于 SELECT 语句的复杂性和重要性,通常将其独立称为 DQL。
核心价值
数据洞察:将存储在磁盘上的原始数据转化为有价值的信息。
灵活筛选:通过条件过滤、排序、分组和聚合,精准获取所需数据子集。
应用基石:几乎所有后端 API、报表系统、数据分析工具都依赖 DQL 获取数据。
指定列查询(推荐):只获取需要的字段,减少网络传输和内存消耗。
SELECT first_name, last_name FROM employees;全列查询(慎用):使用 * 获取所有列。仅在调试或确实需要所有字段时使用。
SELECT * FROM employees;
-- 缺点:如果表结构变化,代码可能失效;传输不必要的大字段(如 TEXT/BLOB)会降低性能。当只需要了解某列有哪些不同的值时,使用 DISTINCT 去重。
-- 查询公司有哪些不同的部门
SELECT DISTINCT department FROM employees;
-- 多列去重:只有当所有指定列的组合都相同时,才视为重复
SELECT DISTINCT department, job_title FROM employees;给列或表起别名,可以提高结果的可读性,或在后续计算中简化引用。
-- 列别名:让输出标题更友好
SELECT
product_name AS item_name,
price AS unit_price,
price * 0.9 AS discounted_price
FROM products;
-- 表别名:在多表连接时非常有用(见高级篇)
SELECT e.first_name, d.dept_name
FROM employees AS e, departments AS d
WHERE e.dept_id = d.id;注意:
AS关键字在列别名中通常可以省略,直接写空格即可,但在表别名中建议保留以增强可读性。
WHERE 子句用于过滤记录,只返回满足条件的行。它是数据筛选的第一道关卡。
重要提示:判断 NULL 值必须使用
IS NULL或IS NOT NULL,不能使用= NULL或!= NULL,因为 NULL 在 SQL 中表示“未知”,与任何值比较(包括它自己)结果都是 UNKNOWN。
用于组合多个条件:
AND:所有条件都必须满足。
OR:只要有一个条件满足即可。
NOT:取反。
-- 案例:查询部门 ID 为 1 且 工资大于 5000 的员工
SELECT * FROM employees
WHERE department_id = 1 AND salary > 5000;
-- 案例:查询状态为 'Shipped' 或 'Delivered' 且 金额大于 1000 的订单
SELECT * FROM orders
WHERE (status = 'Shipped' OR status = 'Delivered') AND total_amount > 1000;用于字符串匹配,支持通配符:
%:匹配任意长度(包括 0 个)的字符。
_:匹配单个字符。
-- 以 'App' 开头的产品 (如 Apple, Application)
SELECT * FROM products WHERE product_name LIKE 'App%';
-- 包含 'phone' 的产品 (如 iPhone, Smartphone)
SELECT * FROM products WHERE product_name LIKE '%phone%';
-- 第二个字母是 'a' 的名字 (如 David, James)
SELECT * FROM users WHERE username LIKE '_a%';性能警告:
LIKE '%keyword%'(前缀通配)会导致索引失效,进行全表扫描。在大数据量下需谨慎使用,或考虑使用全文索引。
默认情况下,数据库返回数据的顺序是不确定的(通常取决于存储引擎和插入顺序)。使用 ORDER BY 可以明确指定排序规则。
-- 按价格升序排列(ASC 可省略,默认为升序)
SELECT product_name, price FROM products ORDER BY price ASC;
-- 按注册日期降序排列(最新的在前)
SELECT * FROM users ORDER BY registration_date DESC;当第一列的值相同时,按第二列排序,依此类推。
-- 先按订单日期升序,同一天内的订单按金额降序排列
SELECT customer_name, order_date, total_amount
FROM orders
ORDER BY order_date ASC, total_amount DESC;基于之前创建的 users 表,我们插入模拟数据并进行多维度查询。
INSERT INTO users (username, password, email, registration_date) VALUES
('user1', 'pass123', 'user1@example.com', '2022-11-15 10:00:00'),
('user2', 'pass456', 'user2@example.com', '2023-02-20 14:30:00'),
('user3', 'pass789', 'user3@example.com', '2023-01-05 09:15:00'),
('alice', 'secure_pwd', 'alice@example.com', '2023-03-10 11:20:00'),
('bob', 'bob_pwd', 'bob@example.com', '2022-12-25 16:45:00'),
('charlie', 'charlie_pwd', 'charlie@example.com', '2023-01-20 08:00:00'),
('david', 'david_pwd', 'david@example.com', '2023-02-14 12:00:00'),
('eve', 'eve_pwd', 'eve@example.com', '2022-10-01 07:30:00');
-- 注:实际插入时 NOW() 会生成当前时间,这里为了演示排序效果,硬编码了不同日期需求:检索所有用户的用户名和注册日期。
SELECT username, registration_date
FROM users;需求:检索所有注册日期在 2023-01-01 之后的用户信息。
SELECT *
FROM users
WHERE registration_date > '2023-01-01 00:00:00';解析:日期时间比较可以直接使用字符串格式,MySQL 会自动转换。
需求:
找出用户名中包含 "er" 的用户。
按注册日期降序排列(最新的在前)。
如果日期相同,按用户名升序排列。
SELECT username, email, registration_date
FROM users
WHERE username LIKE '%er%'
ORDER BY registration_date DESC, username ASC;预期结果:user1, user2, user3, user4... 等包含 'er' 的用户会被选出,并按时间倒序展示。
需求:查看系统中有哪些不同的密码被使用过(仅用于演示 DISTINCT,实际生产严禁明文存密码)。
SELECT DISTINCT password
FROM users;DQL 是开发者最强大的武器,掌握以下原则能让你的查询更高效、更安全:
最小化原则:
只 SELECT 需要的列,避免 SELECT *。
只 WHERE 必要的条件,尽早过滤数据。
索引意识:
在 WHERE, ORDER BY, JOIN 的关联列上建立索引,可以极大提升查询速度。
避免在索引列上进行函数运算(如 WHERE YEAR(date_col) = 2023 会导致索引失效,应改为 WHERE date_col >= '2023-01-01' AND date_col < '2024-01-01')。
NULL 处理:
永远记住 NULL != NULL,使用 IS NULL 进行判断。
可读性:
使用有意义的别名。
格式化 SQL 代码(关键字大写,子句换行),便于团队协作和维护。