SQL 大厂面试题目(由浅入深)
今天给大家带来一份大厂SQL面试覆盖:基础语法 → 复杂查询 → 性能优化 → 架构设计,大家需深入理解执行原理并熟悉实际业务场景的解决方案。
1. 基础查询与过滤
题目:查询 employees 表中所有薪资(salary)大于 10000 且部门编号(dept_id)为 5 的员工姓名(name)和入职日期(hire_date)。
SELECT name, hire_date FROM employees WHERE salary > 10000 AND dept_id = 5;
2. 聚合函数与分组
题目:统计每个部门(dept_id)的平均薪资,并仅显示平均薪资高于 15000 的部门。
SELECT dept_id, AVG(salary) AS avg_salary FROM employees GROUP BY dept_id HAVING AVG(salary) > 15000;
3. 多表连接(JOIN)
题目:查询员工姓名(name)及其所属部门名称(dept_name),表结构为 employees(id, name, dept_id)和 departments(id, dept_name)。
SELECT e.name, d.dept_name FROM employees e JOIN departments d ON e.dept_id = d.id;
4. 子查询与 EXISTS
题目:查询没有订单的客户(customers 表的 id 不在 orders 表的 customer_id 中)。
SELECT c.id, c.name FROM customers c WHERE NOT EXISTS ( SELECT 1 FROM orders o WHERE o.customer_id = c.id );
5. 窗口函数
题目:查询每个部门薪资排名前 3 的员工姓名和薪资。
WITH ranked_employees AS ( SELECT name, salary, dept_id, RANK() OVER (PARTITION BY dept_id ORDER BY salary DESC) AS rank FROM employees ) SELECT name, salary, dept_id FROM ranked_employees WHERE rank '2024-02-10'?答案:
CREATE INDEX idx_customer_order_date ON orders (customer_id, order_date);
原理:联合索引按最左前缀匹配原则,优先按 customer_id 过滤,再按 order_date 范围查询。8. 事务与隔离级别
题目:解释“不可重复读”(Non-Repeatable Read)和“幻读”(Phantom Read)的区别。
答案:
不可重复读:同一事务中两次读取同一行数据,结果不同(由其他事务的 UPDATE 或 DELETE 导致)。
幻读:同一事务中两次查询同一范围的数据,结果行数不同(由其他事务的 INSERT 导致)。
9. 执行计划分析
题目:以下查询的执行计划中出现了 Full Table Scan,如何优化?
SELECT * FROM products WHERE category = 'Electronics' AND price > 1000;
(图片来源网络,侵删)答案:
添加联合索引 (category, price):CREATE INDEX idx_category_price ON products (category, price);
10. 复杂场景设计题目:设计一个数据库表结构,支持用户每日签到(可重复签到但仅第一次有效),并统计某用户最近 30 天的签到次数。
(图片来源网络,侵删)答案:
CREATE TABLE user_checkins ( user_id INT, checkin_date DATE, PRIMARY KEY (user_id, checkin_date) -- 唯一约束避免重复 ); -- 统计最近30天签到次数 SELECT COUNT(*) FROM user_checkins WHERE user_id = 100 AND checkin_date >= CURRENT_DATE - INTERVAL '30 days';
11. 死锁分析与解决题目:两个事务分别执行以下操作,如何发生死锁?
事务1:UPDATE accounts SET balance = balance - 100 WHERE id = 1; UPDATE accounts SET balance = balance + 100 WHERE id = 2;
事务2:UPDATE accounts SET balance = balance - 200 WHERE id = 2; UPDATE accounts SET balance = balance + 200 WHERE id = 1;
答案:
事务1锁定id=1后等待id=2,事务2锁定id=2后等待id=1,形成循环等待。
解决方案:按固定顺序更新(如先更新id小的账户)。
12. 时间窗口统计
题目:统计每小时内订单量最多的前3个小时(表 orders 含字段 order_time)。
WITH hourly_orders AS ( SELECT EXTRACT(HOUR FROM order_time) AS hour, COUNT(*) AS order_count FROM orders GROUP BY EXTRACT(HOUR FROM order_time) ) SELECT hour, order_count FROM hourly_orders ORDER BY order_count DESC LIMIT 3;
13. 数据去重题目:删除 logs 表中重复记录(保留id最小的一条)。
DELETE FROM logs WHERE id NOT IN ( SELECT MIN(id) FROM logs GROUP BY user_id, log_time, content );
14. 分页查询优化题目:优化大表的分页查询 SELECT * FROM users ORDER BY id LIMIT 1000000, 10;。
答案:
使用覆盖索引 + 游标分页:SELECT * FROM users WHERE id > 1000000 ORDER BY id LIMIT 10;
15. 分区表设计题目:如何按时间范围对 sales 表进行分区以优化查询性能?
答案:
-- 按月分区(以 PostgreSQL 为例) CREATE TABLE sales ( sale_id SERIAL, sale_date DATE, amount NUMERIC ) PARTITION BY RANGE (sale_date); CREATE TABLE sales_2023_01 PARTITION OF sales FOR VALUES FROM ('2024-01-10') TO ('2024-02-10');
16. JSON 数据处理题目:从 products 表的 attributes(JSON 字段)中提取颜色(color)和尺寸(size)。
-- 以 MySQL 为例 SELECT attributes->>'$.color' AS color, attributes->>'$.size' AS size FROM products;
17. 高级窗口函数题目:计算每个员工薪资与所在部门平均薪资的差值。
SELECT name, salary, salary - AVG(salary) OVER (PARTITION BY dept_id) AS diff_from_avg FROM employees;
18. 动态SQL与存储过程题目:编写存储过程,根据输入的城市名动态查询 customers 表。
-- 以 PostgreSQL 为例 CREATE OR REPLACE PROCEDURE get_customers_by_city(city_name TEXT) LANGUAGE plpgsql AS $$ BEGIN EXECUTE 'SELECT * FROM customers WHERE city = $1' USING city_name; END; $$;
19. 分布式ID生成方案题目:在分布式系统中,如何设计全局唯一的订单ID?
答案:
雪花算法(Snowflake):时间戳 + 机器ID + 序列号。
UUID:随机生成,但存储效率低。
数据库分段分配:中央数据库分配ID范围给各节点。
20. 数据一致性保障
题目:如何实现“扣减库存时防止超卖”?
答案:
-- 事务内原子操作(以 MySQL 为例) START TRANSACTION; SELECT stock FROM products WHERE id = 100 FOR UPDATE; UPDATE products SET stock = stock - 1 WHERE id = 100 AND stock > 0; COMMIT;