SQL(Structured Query Language)作为关系型数据库的标准查询语言,是每个开发者必须掌握的技能。我在实际数据库开发中,发现80%的日常操作都围绕着查询展开。不同于简单的增删改查,高效的查询语句能显著提升应用性能,减少服务器负载。
SQL查询的核心在于理解关系型数据库的表结构设计。想象一下Excel表格:每张表由行(记录)和列(字段)组成,表与表之间通过主外键建立关联。查询的本质就是从这些结构化数据中提取所需信息的过程。
提示:在编写SQL前,务必先明确三个问题:1)需要哪些字段 2)数据来自哪些表 3)筛选条件是什么
现代数据库系统如MySQL、Oracle、SQL Server等都遵循SQL标准,但各有方言差异。本文示例以标准SQL为基础,适用于大多数关系型数据库,特殊语法差异会特别说明。
最基本的查询语句是SELECT-FROM结构:
sql复制SELECT * FROM employees;
这条语句会返回employees表的所有字段和记录。星号(*)表示选择所有字段,虽然方便但存在隐患:
实际项目中建议明确指定字段:
sql复制SELECT employee_id, first_name, last_name, hire_date
FROM employees;
条件查询通过WHERE子句实现,以下是三种等价的区间查询写法:
sql复制-- 方式1:BETWEEN...AND (推荐)
SELECT * FROM products
WHERE price BETWEEN 50 AND 100;
-- 方式2:使用比较运算符
SELECT * FROM products
WHERE price >= 50 AND price <= 100;
-- 方式3:使用&&(MySQL特有)
SELECT * FROM products
WHERE price >= 50 && price <= 100;
经验:BETWEEN包含边界值,且语句更易读。性能上三种方式在大多数数据库中无显著差异
AND/OR运算符可以组合多个条件:
sql复制-- AND示例:查找30岁以下女性员工
SELECT * FROM employees
WHERE age < 30 AND gender = 'F';
-- OR示例:查找经理或总监级别的员工
SELECT * FROM employees
WHERE title = 'Manager' OR title = 'Director';
复杂逻辑建议使用括号明确优先级:
sql复制SELECT * FROM orders
WHERE (status = 'Shipped' OR status = 'Processing')
AND order_date >= '2023-01-01';
NULL在数据库中表示"未知",不能用常规比较运算符判断:
sql复制-- 错误方式:不会返回任何结果
SELECT * FROM customers
WHERE phone = NULL;
-- 正确方式
SELECT * FROM customers
WHERE phone IS NULL;
-- 查询非NULL记录
SELECT * FROM customers
WHERE phone IS NOT NULL;
LIKE操作符配合通配符实现模糊查询:
sql复制-- %表示任意多个字符
SELECT * FROM products
WHERE name LIKE '%Pro%'; -- 包含"Pro"
-- _表示单个字符
SELECT * FROM users
WHERE username LIKE 'j_hn'; -- 如'john'、'jahn'
注意:模糊查询通常无法使用索引,大数据表慎用。前导通配符(如'%abc')尤其耗性能
DISTINCT去除重复值:
sql复制-- 获取所有不重复的部门ID
SELECT DISTINCT department_id FROM employees;
ORDER BY控制结果排序:
sql复制-- 默认升序(ASC)
SELECT * FROM products ORDER BY price;
-- 降序排列
SELECT * FROM products ORDER BY price DESC;
-- 多列排序:先按类别升序,同类别按价格降序
SELECT * FROM products
ORDER BY category ASC, price DESC;
SQL提供5个核心聚合函数:
sql复制-- 计算平均值
SELECT AVG(salary) FROM employees;
-- 求和
SELECT SUM(quantity) FROM order_items;
-- 最大值/最小值
SELECT MAX(price), MIN(price) FROM products;
-- 计数
SELECT COUNT(*) FROM customers; -- 所有行数
SELECT COUNT(email) FROM customers; -- 非NULL的email数
GROUP BY将数据分组后应用聚合函数:
sql复制-- 每个部门的平均薪资
SELECT department_id, AVG(salary)
FROM employees
GROUP BY department_id;
HAVING子句过滤分组结果:
sql复制-- 平均薪资超过10000的部门
SELECT department_id, AVG(salary) as avg_salary
FROM employees
GROUP BY department_id
HAVING avg_salary > 10000;
关键区别:WHERE过滤行,HAVING过滤分组。WHERE在GROUP BY前执行,HAVING在后
内连接只返回两表匹配的记录:
sql复制-- 显式内连接(推荐)
SELECT o.order_id, c.customer_name
FROM orders o
INNER JOIN customers c ON o.customer_id = c.customer_id;
-- 隐式内连接(老式语法)
SELECT o.order_id, c.customer_name
FROM orders o, customers c
WHERE o.customer_id = c.customer_id;
左外连接保留左表所有记录:
sql复制-- 即使没有订单的客户也会显示
SELECT c.customer_name, o.order_id
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id;
右外连接保留右表所有记录:
sql复制-- 即使没有客户信息的订单也会显示
SELECT c.customer_name, o.order_id
FROM customers c
RIGHT JOIN orders o ON c.customer_id = o.customer_id;
实际开发中,LEFT JOIN使用更普遍。RIGHT JOIN通常可以改写为LEFT JOIN
返回单个值的子查询:
sql复制-- 查找高于平均薪资的员工
SELECT * FROM employees
WHERE salary > (SELECT AVG(salary) FROM employees);
返回单列多行的子查询,常与IN配合:
sql复制-- 查找有订单的客户
SELECT * FROM customers
WHERE customer_id IN (
SELECT DISTINCT customer_id FROM orders
);
返回单行多列的子查询:
sql复制-- 查找与特定员工部门和职位相同的其他员工
SELECT * FROM employees
WHERE (department_id, job_title) = (
SELECT department_id, job_title
FROM employees
WHERE employee_id = 123
);
检查子查询是否返回结果:
sql复制-- 查找有订单的客户(比IN更高效)
SELECT * FROM customers c
WHERE EXISTS (
SELECT 1 FROM orders o
WHERE o.customer_id = c.customer_id
);
不同数据库的分页语法差异较大:
sql复制-- MySQL
SELECT * FROM products
ORDER BY product_id
LIMIT 10 OFFSET 20; -- 第3页,每页10条
-- Oracle 12c+
SELECT * FROM products
ORDER BY product_id
OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;
-- SQL Server
SELECT * FROM products
ORDER BY product_id
OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;
性能提示:大数据量分页避免使用OFFSET,改用"上一页/下一页"模式:
sql复制-- 比OFFSET更高效的方式
SELECT * FROM products
WHERE product_id > last_seen_id
ORDER BY product_id
LIMIT 10;
索引使用原则:
WHERE YEAR(create_time) = 2023执行计划分析:
sql复制EXPLAIN SELECT * FROM orders WHERE customer_id = 100;
通过执行计划可以查看SQL是否使用了索引,是否存在全表扫描等问题
批量操作优化:
sql复制-- 低效方式
INSERT INTO log (message) VALUES ('msg1');
INSERT INTO log (message) VALUES ('msg2');
-- 高效方式
INSERT INTO log (message) VALUES ('msg1'), ('msg2');
**避免SELECT ***:
事务使用原则:
sql复制BEGIN TRANSACTION;
-- 一系列操作
COMMIT;
-- 或出错时 ROLLBACK;
事务应尽可能短,避免长时间持有锁
语法错误:
性能问题:
连接查询问题:
分组聚合问题:
子查询问题:
掌握这些SQL查询技巧后,面对大多数数据检索需求都能游刃有余。实际开发中,建议先在测试环境验证复杂查询的正确性和性能,再应用到生产环境。