Top 20 SQL面试题及答案
概述
通过这些Top 20 SQL面试题及答案来破解您的SQL面试。包含详细解释、示例和关键差异,适用于应届生和有经验的候选人。
🔹 1. 什么是SQL?
答案:
SQL(结构化查询语言)是用于管理和操作数据库的标准语言。
示例:
- SELECT * FROM employees;
🔹 2. DELETE、TRUNCATE和DROP之间有什么区别?
|
命令
|
描述
|
可回滚
|
影响结构
|
|
DELETE
|
删除选定的行
|
是
|
否
|
|
TRUNCATE
|
快速删除所有行
|
否
|
否
|
|
DROP
|
删除整个表
|
否
|
是
|
🔹 3. SQL语句有哪些不同类型?
答案:
DDL – 数据定义语言(CREATE、ALTER、DROP)
DML – 数据操作语言(INSERT、UPDATE、DELETE)
DQL – 数据查询语言(SELECT)
DCL – 数据控制语言(GRANT、REVOKE)
TCL – 事务控制语言(COMMIT、ROLLBACK)
🔹 4. 什么是主键?
答案:
主键是唯一标识表中每一行的列(或列集合)。
示例:
- CREATE TABLE students (
- student_id INT PRIMARY KEY,
- name VARCHAR(50)
- );
🔹 5. 什么是外键?
答案:
外键是一个表中的列,指向另一个表中的主键,用于建立关系。
示例:
- FOREIGN KEY (student_id) REFERENCES students(student_id)
🔹 6. WHERE和HAVING之间有什么区别?
|
子句
|
用例
|
作用于
|
|
WHERE
|
在分组前过滤行
|
行
|
|
HAVING
|
在GROUP BY后过滤
|
聚合数据
|
示例:
- SELECT dept, COUNT(*) FROM employees
- GROUP BY dept
- HAVING COUNT(*) > 5;
🔹 7. SQL中的JOIN是什么?
答案:
JOIN用于组合两个或多个表中的行。
|
连接类型
|
描述
|
|
INNER JOIN
|
匹配两个表中的行
|
|
LEFT JOIN
|
左表的所有行 + 右表的匹配行
|
|
RIGHT JOIN
|
右表的所有行 + 左表的匹配行
|
|
FULL JOIN
|
两个表中所有匹配 + 不匹配的行
|
示例:
- SELECT e.name, d.name
- FROM employees e
- INNER JOIN departments d ON e.dept_id = d.id;
🔹 8. UNION和UNION ALL之间有什么区别?
|
关键字
|
重复项
|
更快
|
|
UNION
|
移除
|
否
|
|
UNION ALL
|
保留
|
是
|
🔹 9. 什么是规范化?
答案:
规范化是减少数据冗余和提高数据完整性的过程。
范式:
1NF:原子列
2NF:无部分依赖
3NF:无传递依赖
🔹 10. 什么是子查询?
答案:
子查询是嵌套在另一个查询中的查询。
示例:
- SELECT name FROM students
- WHERE id IN (SELECT student_id FROM enrollments);
🔹 11. IN和EXISTS之间有什么区别?
IN:检查列表或子查询中的值。
EXISTS:检查子查询是否返回至少一行。
🔹 12. 什么是索引?
答案:
索引通过允许更快的搜索和检索来提高查询性能。
示例:
- CREATE INDEX idx_name ON employees(name);
🔹 13. 什么是视图?
答案:
视图是基于SQL查询的虚拟表。
示例:
- CREATE VIEW active_employees AS
- SELECT * FROM employees WHERE status = 'active';
🔹 14. 什么是存储过程?
答案:
存储过程是预编译的SQL语句组。
示例:
- CREATE PROCEDURE GetEmployees()
- AS
- BEGIN
- SELECT * FROM employees;
- END;
🔹 15. CHAR和VARCHAR之间有什么区别?
|
类型
|
固定/可变
|
存储
|
|
CHAR(n)
|
固定长度
|
始终n字节
|
|
VARCHAR(n)
|
可变长度
|
最多n字节
|
🔹 16. SQL中的聚合函数有哪些?
答案:
聚合函数对多行执行计算。
|
函数
|
用途
|
|
SUM()
|
求和
|
|
AVG()
|
平均值
|
|
COUNT()
|
计数行
|
|
MAX()
|
最大值
|
|
MIN()
|
最小值
|
🔹 17. 什么是约束?
答案:
约束对数据列强制执行规则。
NOT NULL
UNIQUE
PRIMARY KEY
FOREIGN KEY
CHECK
DEFAULT
🔹 18. BETWEEN和IN之间有什么区别?
BETWEEN检查范围。
IN检查特定值。
示例:
- -- BETWEEN
- SELECT * FROM students WHERE age BETWEEN 18 AND 25;
- -- IN
- SELECT * FROM students WHERE city IN ('Delhi', 'Mumbai');
🔹 19. SQL中的事务是什么?
答案:
事务是一个工作单元,要么完全完成,要么完全失败。
命令:
BEGIN TRANSACTION
COMMIT
ROLLBACK
🔹 20. 如何优化慢SQL查询?
答案:
使用索引
避免SELECT
正确使用JOIN
避免不必要的子查询
使用EXPLAIN或QUERY PLAN分析
详细代码示例
数据库表结构示例
- -- 创建学生表
- CREATE TABLE students (
- student_id INT PRIMARY KEY AUTO_INCREMENT,
- name VARCHAR(100) NOT NULL,
- email VARCHAR(100) UNIQUE,
- age INT CHECK (age >= 0),
- department_id INT,
- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
- FOREIGN KEY (department_id) REFERENCES departments(department_id)
- );
- -- 创建部门表
- CREATE TABLE departments (
- department_id INT PRIMARY KEY AUTO_INCREMENT,
- name VARCHAR(50) NOT NULL,
- location VARCHAR(100)
- );
- -- 创建课程表
- CREATE TABLE courses (
- course_id INT PRIMARY KEY AUTO_INCREMENT,
- name VARCHAR(100) NOT NULL,
- credits INT DEFAULT 3,
- instructor_id INT
- );
- -- 创建学生课程关联表
- CREATE TABLE student_courses (
- student_id INT,
- course_id INT,
- grade DECIMAL(3,2),
- enrollment_date DATE,
- PRIMARY KEY (student_id, course_id),
- FOREIGN KEY (student_id) REFERENCES students(student_id),
- FOREIGN KEY (course_id) REFERENCES courses(course_id)
- );
复杂查询示例
- -- 1. 使用JOIN查询学生和部门信息
- SELECT
- s.name AS student_name,
- s.email,
- d.name AS department_name
- FROM students s
- LEFT JOIN departments d ON s.department_id = d.department_id
- WHERE s.age BETWEEN 18 AND 25;
- -- 2. 使用子查询查找平均分高于班级平均分的学生
- SELECT
- s.name,
- sc.grade
- FROM students s
- JOIN student_courses sc ON s.student_id = sc.student_id
- WHERE sc.grade > (
- SELECT AVG(grade)
- FROM student_courses
- WHERE course_id = sc.course_id
- );
- -- 3. 使用聚合函数和GROUP BY
- SELECT
- d.name AS department,
- COUNT(s.student_id) AS student_count,
- AVG(s.age) AS avg_age
- FROM departments d
- LEFT JOIN students s ON d.department_id = s.department_id
- GROUP BY d.department_id, d.name
- HAVING COUNT(s.student_id) > 5;
- -- 4. 使用窗口函数
- SELECT
- s.name,
- sc.grade,
- RANK() OVER (PARTITION BY sc.course_id ORDER BY sc.grade DESC) AS rank_in_course
- FROM students s
- JOIN student_courses sc ON s.student_id = sc.student_id;
- -- 5. 使用CTE(公用表表达式)
- WITH high_performers AS (
- SELECT student_id, AVG(grade) as avg_grade
- FROM student_courses
- GROUP BY student_id
- HAVING AVG(grade) >= 3.5
- )
- SELECT
- s.name,
- hp.avg_grade
- FROM students s
- JOIN high_performers hp ON s.student_id = hp.student_id;
存储过程和函数示例
- -- 创建存储过程
- DELIMITER //
- CREATE PROCEDURE GetStudentInfo(IN student_id_param INT)
- BEGIN
- SELECT
- s.name,
- s.email,
- d.name AS department,
- COUNT(sc.course_id) AS enrolled_courses,
- AVG(sc.grade) AS average_grade
- FROM students s
- LEFT JOIN departments d ON s.department_id = d.department_id
- LEFT JOIN student_courses sc ON s.student_id = sc.student_id
- WHERE s.student_id = student_id_param
- GROUP BY s.student_id, s.name, s.email, d.name;
- END //
- DELIMITER ;
- -- 创建函数
- DELIMITER //
- CREATE FUNCTION GetStudentGrade(student_id_param INT, course_id_param INT)
- RETURNS DECIMAL(3,2)
- READS SQL DATA
- DETERMINISTIC
- BEGIN
- DECLARE student_grade DECIMAL(3,2);
- SELECT grade INTO student_grade
- FROM student_courses
- WHERE student_id = student_id_param
- AND course_id = course_id_param;
- RETURN COALESCE(student_grade, 0.00);
- END //
- DELIMITER ;
- -- 使用存储过程
- CALL GetStudentInfo(1);
- -- 使用函数
- SELECT GetStudentGrade(1, 101) AS grade;
索引和性能优化示例
- -- 创建复合索引
- CREATE INDEX idx_student_dept_age ON students(department_id, age);
- -- 创建部分索引(MySQL 8.0+)
- CREATE INDEX idx_active_students ON students(name) WHERE age >= 18;
- -- 分析查询性能
- EXPLAIN SELECT * FROM students WHERE department_id = 1 AND age > 20;
- -- 查看索引使用情况
- SHOW INDEX FROM students;
- -- 优化查询示例
- -- 不好的查询
- SELECT * FROM students WHERE name LIKE '%John%';
- -- 优化的查询
- SELECT student_id, name, email FROM students
- WHERE name LIKE 'John%'
- LIMIT 10;
事务处理示例
- -- 开始事务
- START TRANSACTION;
- -- 插入新学生
- INSERT INTO students (name, email, age, department_id)
- VALUES ('张三', 'zhangsan@example.com', 20, 1);
- -- 获取新插入的学生ID
- SET @new_student_id = LAST_INSERT_ID();
- -- 为学生注册课程
- INSERT INTO student_courses (student_id, course_id, grade, enrollment_date)
- VALUES (@new_student_id, 101, NULL, CURDATE());
- -- 提交事务
- COMMIT;
- -- 错误处理示例
- START TRANSACTION;
- BEGIN
- DECLARE EXIT HANDLER FOR SQLEXCEPTION
- BEGIN
- ROLLBACK;
- SELECT 'Transaction failed' AS message;
- END;
- INSERT INTO students (name, email, age, department_id)
- VALUES ('李四', 'lisi@example.com', 22, 1);
- INSERT INTO student_courses (student_id, course_id, grade, enrollment_date)
- VALUES (LAST_INSERT_ID(), 101, NULL, CURDATE());
- COMMIT;
- SELECT 'Transaction successful' AS message;
- END;
面试技巧
1. 准备阶段
复习基本的SQL语法和概念
练习编写复杂查询
理解数据库设计原则
熟悉性能优化技巧
2. 面试中
仔细阅读问题要求
先思考再编码
解释你的思路
考虑边界情况
讨论性能影响
3. 常见陷阱
忘记处理NULL值
忽略索引的重要性
不熟悉事务处理
缺乏对数据类型的理解
总结
这20个SQL面试题涵盖了从基础概念到高级特性的各个方面:
基础概念:SQL语法、数据类型、约束
查询操作:SELECT、JOIN、子查询、聚合函数
数据操作:INSERT、UPDATE、DELETE、事务
性能优化:索引、查询优化、存储过程
数据库设计:规范化、主键、外键
掌握这些概念将帮助您在SQL面试中表现出色,并在实际工作中编写高效、可维护的数据库代码。
