里豳朝 发表于 2025-9-17 22:11:58

SQL练习题(持续更新中)

测试表创建
-- 1. 部门表(departments)
CREATE TABLE IF NOT EXISTS departments (
    dept_id INT PRIMARY KEY AUTO_INCREMENT COMMENT '部门ID,主键自增',
    dept_name VARCHAR(50) NOT NULL UNIQUE COMMENT '部门名称,唯一不可重复',
    location VARCHAR(100) COMMENT '部门所在地',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间'
) COMMENT '公司部门信息表';

-- 2. 员工表(employees)
CREATE TABLE IF NOT EXISTS employees (
    id INT PRIMARY KEY AUTO_INCREMENT COMMENT '员工ID,主键自增',
    name VARCHAR(50) NOT NULL COMMENT '员工姓名',
    gender ENUM('男', '女', '未知') DEFAULT '未知' COMMENT '员工性别',
    department VARCHAR(50) COMMENT '所属部门(关联departments表的dept_name)',
    hire_date DATE NOT NULL COMMENT '入职日期',
    phone VARCHAR(20) UNIQUE COMMENT '手机号码,唯一',
    email VARCHAR(100) UNIQUE COMMENT '邮箱,唯一',
    manager_id INT COMMENT '直属上级ID(自关联,关联本表的id)',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',
    FOREIGN KEY (department) REFERENCES departments(dept_name) ON UPDATE CASCADE,
    FOREIGN KEY (manager_id) REFERENCES employees(id) ON DELETE SET NULL
) COMMENT '公司员工信息表';

-- 3. 技能表(skills)
CREATE TABLE IF NOT EXISTS skills (
    skill_id INT PRIMARY KEY AUTO_INCREMENT COMMENT '技能ID,主键自增',
    skill_name VARCHAR(50) NOT NULL UNIQUE COMMENT '技能名称,唯一',
    skill_type VARCHAR(30) COMMENT '技能类型(如:编程语言、工具等)',
    description VARCHAR(200) COMMENT '技能描述'
) COMMENT '技能信息表';

-- 4. 员工-技能中间表(employee_skills)
CREATE TABLE IF NOT EXISTS employee_skills (
    id INT PRIMARY KEY AUTO_INCREMENT COMMENT '记录ID,主键自增',
    employee_id INT NOT NULL COMMENT '员工ID,关联employees表',
    skill_id INT NOT NULL COMMENT '技能ID,关联skills表',
    proficiency INT CHECK (proficiency BETWEEN 1 AND 5) COMMENT '熟练程度(1-5,5为最高)',
    learned_date DATE COMMENT '掌握该技能的日期',
    UNIQUE KEY uk_employee_skill (employee_id, skill_id),
    FOREIGN KEY (employee_id) REFERENCES employees(id) ON DELETE CASCADE,
    FOREIGN KEY (skill_id) REFERENCES skills(skill_id) ON DELETE CASCADE
) COMMENT '员工与技能的关联表(多对多)';

-- 5. 薪资记录表(salary_records)
CREATE TABLE IF NOT EXISTS salary_records (
    record_id INT PRIMARY KEY AUTO_INCREMENT COMMENT '记录ID,主键自增',
    employee_id INT NOT NULL COMMENT '员工ID,关联员工表',
    basic_salary DECIMAL(10, 2) NOT NULL CHECK (basic_salary >= 0) COMMENT '基本工资',
    bonus DECIMAL(10, 2) DEFAULT 0 CHECK (bonus >= 0) COMMENT '奖金',
    subsidy DECIMAL(10, 2) DEFAULT 0 CHECK (subsidy >= 0) COMMENT '补贴',
    total_salary DECIMAL(10, 2) GENERATED ALWAYS AS (basic_salary + bonus + subsidy) STORED COMMENT '总薪资(自动计算)',
    effective_date DATE NOT NULL COMMENT '生效日期',
    expire_date DATE COMMENT '失效日期(NULL表示当前生效)',
    reason VARCHAR(200) COMMENT '薪资调整原因',
    created_by VARCHAR(50) COMMENT '操作人',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',
    CONSTRAINT uk_employee_effective UNIQUE (employee_id, effective_date),
    FOREIGN KEY (employee_id) REFERENCES employees(id) ON DELETE CASCADE
) COMMENT '员工薪资变动记录表';插入数据
-- 先插入部门数据(因为员工表依赖部门表)
INSERT INTO departments (dept_name, location) VALUES
('技术部', '北京'),
('市场部', '上海'),
('人事部', '广州'),
('财务部', '深圳');

-- 插入员工数据
-- 插入部门数据(使用IGNORE避免重复)
INSERT IGNORE INTO departments (dept_name, location) VALUES
('技术部', '北京海淀区'),
('市场部', '上海浦东新区'),
('人事部', '广州天河区'),
('财务部', '深圳南山区'),
('运营部', '杭州西湖区');

-- 插入员工数据(包含部门关联和上级关联)
INSERT IGNORE INTO employees (id, name, gender, department, hire_date, phone, email, manager_id) VALUES
(1, '张三', '男', '技术部', '2020-01-15', '13800138001', 'zhangsan@example.com', NULL),
(2, '李四', '女', '市场部', '2021-03-20', '13900139002', 'lisi@example.com', NULL),
(3, '王五', '男', '技术部', '2019-11-05', '13700137003', 'wangwu@example.com', 1),
(4, '赵六', '女', '人事部', '2022-05-10', '13600136004', 'zhaoliu@example.com', NULL),
(5, '钱七', '男', '技术部', '2021-09-30', '13500135005', 'qianqi@example.com', 1),
(6, '孙八', '女', '财务部', '2020-07-22', '13400134006', 'sunba@example.com', NULL),
(7, '周九', '男', '市场部', '2022-01-18', '13300133007', 'zhoujiu@example.com', 2),
(8, '吴十', '女', '运营部', '2021-06-05', '13200132008', 'wushi@example.com', NULL),
(9, '郑十一', '男', '财务部', '2023-02-10', '13100131009', 'zheng11@example.com', 6),
(10, '王十二', '女', '运营部', '2022-09-15', '13001300130', 'wang12@example.com', 8);

-- 插入技能数据
INSERT IGNORE INTO skills (skill_id, skill_name, skill_type, description) VALUES
(1, 'Java', '编程语言', '面向对象的编程语言'),
(2, 'Python', '编程语言', '简洁易学的脚本语言'),
(3, 'MySQL', '数据库', '关系型数据库管理系统'),
(4, 'JavaScript', '编程语言', '前端开发主要语言'),
(5, 'Excel', '办公软件', '数据处理与分析工具'),
(6, 'PPT', '办公软件', '演示文稿制作工具'),
(7, 'Vue', '前端框架', '渐进式JavaScript框架'),
(8, 'Spring Boot', '后端框架', 'Java开发框架'),
(9, '数据分析', '业务能力', '数据挖掘与分析能力'),
(10, '项目管理', '管理能力', '项目规划与执行能力');

-- 插入员工-技能关联数据
INSERT IGNORE INTO employee_skills (employee_id, skill_id, proficiency, learned_date) VALUES
(1, 1, 5, '2018-06-10'),-- 张三:Java(熟练5)
(1, 3, 4, '2019-01-15'),-- 张三:MySQL(熟练4)
(1, 8, 5, '2019-05-20'),-- 张三:Spring Boot(熟练5)
(3, 1, 4, '2019-03-20'),-- 王五:Java(熟练4)
(3, 2, 3, '2020-05-10'),-- 王五:Python(熟练3)
(3, 3, 3, '2019-12-05'),-- 王五:MySQL(熟练3)
(5, 1, 3, '2021-02-28'),-- 钱七:Java(熟练3)
(5, 4, 2, '2022-01-15'),-- 钱七:JavaScript(熟练2)
(5, 7, 2, '2022-03-10'),-- 钱七:Vue(熟练2)
(2, 5, 4, '2020-11-05'),-- 李四:Excel(熟练4)
(2, 6, 5, '2019-09-30'),-- 李四:PPT(熟练5)
(2, 9, 4, '2021-01-20'),-- 李四:数据分析(熟练4)
(4, 5, 5, '2021-07-20'),-- 赵六:Excel(熟练5)
(4, 10, 3, '2022-08-15'), -- 赵六:项目管理(熟练3)
(6, 3, 4, '2019-05-15'),-- 孙八:MySQL(熟练4)
(6, 5, 4, '2018-11-10'),-- 孙八:Excel(熟练4)
(7, 6, 3, '2021-05-10'),-- 周九:PPT(熟练3)
(7, 9, 2, '2022-03-20'),-- 周九:数据分析(熟练2)
(8, 10, 4, '2020-08-05'), -- 吴十:项目管理(熟练4)
(10, 9, 3, '2022-11-10'); -- 王十二:数据分析(熟练3)

-- 插入薪资记录数据(包含初始薪资和调整记录)
INSERT IGNORE INTO salary_records (record_id, employee_id, basic_salary, bonus, subsidy, effective_date, expire_date, reason, created_by) VALUES
-- 张三的薪资记录
(1, 1, 7000, 500, 500, '2020-01-15', '2021-12-31', '入职初始薪资', 'admin'),
(2, 1, 8000, 800, 500, '2022-01-01', NULL, '年度调薪', 'admin'),

-- 李四的薪资记录
(3, 2, 6000, 300, 200, '2021-03-20', '2022-06-30', '入职初始薪资', 'admin'),
(4, 2, 6500, 400, 200, '2022-07-01', NULL, '半年度调薪', 'admin'),

-- 王五的薪资记录
(5, 3, 8500, 500, 200, '2019-11-05', '2021-05-31', '入职初始薪资', 'admin'),
(6, 3, 9200, 600, 400, '2021-06-01', NULL, '晋升调薪', 'admin'),

-- 赵六的薪资记录
(7, 4, 5500, 200, 100, '2022-05-10', NULL, '入职初始薪资', 'admin'),

-- 钱七的薪资记录
(8, 5, 7000, 300, 200, '2021-09-30', '2023-02-28', '入职初始薪资', 'admin'),
(9, 5, 7500, 400, 200, '2023-03-01', NULL, '年度调薪', 'admin'),

-- 孙八的薪资记录
(10, 6, 7200, 500, 300, '2020-07-22', '2022-12-31', '入职初始薪资', 'admin'),
(11, 6, 7800, 600, 300, '2023-01-01', NULL, '年度调薪', 'admin'),

-- 周九的薪资记录
(12, 7, 6200, 200, 100, '2022-01-18', '2023-06-30', '入职初始薪资', 'admin'),
(13, 7, 6800, 300, 100, '2023-07-01', NULL, '年度调薪', 'admin'),

-- 吴十的薪资记录
(14, 8, 6500, 400, 300, '2021-06-05', '2022-11-30', '入职初始薪资', 'admin'),
(15, 8, 7000, 500, 300, '2022-12-01', NULL, '年度调薪', 'admin'),

-- 郑十一的薪资记录
(16, 9, 5800, 200, 100, '2023-02-10', NULL, '入职初始薪资', 'admin'),

-- 王十二的薪资记录
(17, 10, 6000, 300, 200, '2022-09-15', '2023-08-31', '入职初始薪资', 'admin'),
(18, 10, 6300, 300, 200, '2023-09-01', NULL, '年度调薪', 'admin');   一、基础查询与条件筛选(单表操作)


[*]题目:查询所有部门的名称和所在地,并按部门名称升序排序。SELECT dept_name, location
FROM departments
ORDER BY dept_name ASC;
[*]题目:查询技术部入职时间在2021年及之后的员工,显示姓名、入职日期和手机号。SELECT name, hire_date, phone
FROM employees
WHERE department = '技术部'
AND hire_date >= '2021-01-01';
[*]题目:查询薪资记录中总薪资(total_salary)在8000-10000之间的记录,显示员工ID、总薪资和生效日期。SELECT employee_id, total_salary, effective_date
FROM salary_records
WHERE total_salary BETWEEN 8000 AND 10000;
二、聚合函数与分组查询


[*]题目:统计每个部门的员工人数,显示部门名称和人数,过滤出人数≥3的部门。
SELECT department AS 部门名称,
       COUNT(*) AS 员工人数
FROM employees
GROUP BY department
HAVING COUNT(*) >= 3;
[*]题目:计算各部门当前生效薪资的平均值(取expire_date IS NULL的记录),显示部门名称和平均薪资(保留2位小数)。
SELECT e.department AS 部门名称,
       ROUND(AVG(sr.total_salary), 2) AS 平均薪资
FROM employees e
JOIN salary_records sr ON e.id = sr.employee_id
WHERE sr.expire_date IS NULL
GROUP BY e.department;
[*]题目:统计每种技能的掌握人数,按人数从多到少排序,包含无人掌握的技能(显示0)。
SELECT
s.skill_name AS 技能名称,
COUNT(es.employee_id) AS 掌握人数
FROM skills s
LEFT JOIN employee_skills es ON s.skill_id = es.skill_id
GROUP BY s.skill_name;
三、连接查询(多表关联)


[*]题目:查询所有员工的姓名、所属部门名称及部门所在地,包括未分配部门的员工(若有)。
SELECT
e.name AS 员工姓名,
d.dept_name AS 部门名称,
d.location AS 部门所在地
FROM employees e
LEFT JOIN departments d ON e.department = d.dept_name;
[*]题目:查询掌握“Java”技能的员工姓名、部门及熟练程度,要求熟练程度≥4。
SELECT
e.name AS 员工姓名,
e.department AS 部门,
es.proficiency AS 熟练程度
FROM employees e
JOIN employee_skills es ON e.id = es.employee_id
JOIN skills s ON es.skill_id = s.skill_id
WHERE s.skill_name = 'Java'
AND es.proficiency >= 4;
[*]题目:查询2023年有薪资调整的员工姓名及调整前后的总薪资(需显示“调整前薪资”和“调整后薪资”)。
SELECT
e.name AS 员工姓名,
prev.total_salary AS 调整前薪资,
curr.total_salary AS 调整后薪资,
curr.effective_date AS 调整日期
FROM employees e
JOIN salary_records curr ON e.id = curr.employee_id
JOIN salary_records prev ON e.id = prev.employee_id
AND prev.expire_date = curr.effective_date - INTERVAL 1 DAY
WHERE YEAR(curr.effective_date) = 2023;
四、子查询与嵌套查询


[*]题目:查询薪资高于本部门平均薪资的员工,显示姓名、部门和当前总薪资。
SELECT
e.name AS 员工姓名,
e.department AS 部门,
sr.total_salary AS 当前总薪资
FROM employees e
JOIN salary_records sr ON e.id = sr.employee_id
WHERE sr.expire_date IS NULL
AND sr.total_salary > (
    SELECT AVG(sr2.total_salary)
    FROM employees e2
    JOIN salary_records sr2 ON e2.id = sr2.employee_id
    WHERE sr2.expire_date IS NULL
      AND e2.department = e.department
);
[*]题目:找出同时掌握“Java”和“MySQL”两种技能的员工姓名。
SELECT e.name AS 员工姓名
FROM employees e
WHERE EXISTS (
SELECT 1
FROM employee_skills es
JOIN skills s ON es.skill_id = s.skill_id
WHERE es.employee_id = e.id AND s.skill_name = 'Java'
)
AND EXISTS (
SELECT 1
FROM employee_skills es
JOIN skills s ON es.skill_id = s.skill_id
WHERE es.employee_id = e.id AND s.skill_name = 'MySQL'
);
[*]题目:查询各部门中薪资最高的员工,显示部门名称、员工姓名和最高薪资。
SELECT
dept_name AS 部门名称,
name AS 员工姓名,
max_salary AS 最高薪资
FROM (
SELECT
    e.department AS dept_name,
    e.name,
    sr.total_salary,
    MAX(sr.total_salary) OVER (PARTITION BY e.department) AS max_salary
FROM employees e
JOIN salary_records sr ON e.id = sr.employee_id
WHERE sr.expire_date IS NULL
) AS sub
WHERE total_salary = max_salary
ORDER BY dept_name;
五、窗口函数与高级查询


[*]题目:给每个部门的员工按当前薪资从高到低排名,显示姓名、部门、薪资和排名(同一部门内排名)。
SELECT
e.name AS 员工姓名,
e.department AS 部门,
sr.total_salary AS 薪资,
RANK() OVER (PARTITION BY e.department ORDER BY sr.total_salary DESC) AS 排名
FROM employees e
JOIN salary_records sr ON e.id = sr.employee_id
WHERE sr.expire_date IS NULL
ORDER BY e.department, 排名;
[*]题目:计算每个员工的薪资较上一次调整的涨幅比例,显示姓名、调整日期和涨幅(保留1位小数)。
SELECT
e.name AS 员工姓名,
curr.effective_date AS 调整日期,
curr.total_salary AS 当前薪资,
prev.total_salary AS 上次薪资,
ROUND(
    (curr.total_salary - prev.total_salary) / prev.total_salary * 100,
    1
) AS 涨幅百分比
FROM employees e
JOIN salary_records curr ON e.id = curr.employee_id
JOIN salary_records prev ON e.id = prev.employee_id
AND prev.expire_date = curr.effective_date - INTERVAL 1 DAY
ORDER BY e.name, 调整日期;
[*]题目:统计各部门中不同技能类型的员工数量,例如“技术部”的“编程语言”技能有多少人掌握。
SELECT
e.department AS 部门,
s.skill_type AS 技能类型,
COUNT(DISTINCT e.id) AS 员工数量
FROM employees e
LEFT JOIN employee_skills es ON e.id = es.employee_id
LEFT JOIN skills s ON es.skill_id = s.skill_id
GROUP BY e.department, s.skill_type
ORDER BY e.department, s.skill_type;
六、综合场景查询


[*]题目:查询“张三”的所有上级(包含多级上级),显示上级姓名和职位关系(如“直接上级”、“间接上级”)。
WITH RECURSIVE manager_chain AS (
-- 起始节点:张三的直接上级
SELECT
    m.id AS manager_id,
    m.name AS manager_name,
    1 AS level,
    '直接上级' AS relation
FROM employees e
LEFT JOIN employees m ON e.manager_id = m.id
WHERE e.name = '张三'

UNION ALL

-- 递归查询:上级的上级
SELECT
    m2.id AS manager_id,
    m2.name AS manager_name,
    mc.level + 1 AS level,
    CONCAT('间接上级(', mc.level + 1, '级)') AS relation
FROM manager_chain mc
JOIN employees m2 ON mc.manager_id = m2.manager_id
WHERE m2.id IS NOT NULL
)
SELECT manager_name AS 上级姓名, relation AS 职位关系
FROM manager_chain;
[*]题目:分析技能掌握情况与薪资的关系:计算掌握3种及以上技能的员工平均薪资,与掌握不足3种技能的员工平均薪资对比。
SELECT
CASE
    WHEN skill_count >= 3 THEN '掌握3种及以上技能'
    ELSE '掌握不足3种技能'
END AS 技能掌握情况,
ROUND(AVG(total_salary), 2) AS 平均薪资
FROM (
-- 子查询:统计每个员工的技能数量
SELECT
    e.id,
    e.name,
    COUNT(DISTINCT es.skill_id) AS skill_count,
    sr.total_salary
FROM employees e
LEFT JOIN employee_skills es ON e.id = es.employee_id
JOIN salary_records sr ON e.id = sr.employee_id
WHERE sr.expire_date IS NULL
GROUP BY e.id, e.name, sr.total_salary
) AS skill_stats
GROUP BY 技能掌握情况;
[*]题目:查询各部门近2年(2022-2023)薪资调整总次数,并计算调整次数最多的部门。
SELECT
部门,
调整次数
FROM (
SELECT
    e.department AS 部门,
    COUNT(sr.record_id) AS 调整次数,
    RANK() OVER (ORDER BY COUNT(sr.record_id) DESC) AS rnk
FROM employees e
JOIN salary_records sr ON e.id = sr.employee_id
WHERE YEAR(sr.effective_date) BETWEEN 2022 AND 2023
GROUP BY e.department
) AS dept_adjust
WHERE rnk = 1;

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: SQL练习题(持续更新中)