在线练习mysql(MySQL 表关联查询完全指南)

在线练习mysql(MySQL 表关联查询完全指南)
MySQL 表关联查询完全指南

#头条创作训练营#


MySQL 表关联查询完全指南

一、MySQL 关联查询基础语法

1. 内连接 (INNER JOIN) - 最常用

返回两个表中匹配的行(交集)

```sql

-- 基础语法

SELECT *

FROM 表1

INNER JOIN 表2 ON 表1.关联字段 = 表2.关联字段;

-- 实际示例:员工表关联部门表

SELECT e.emp_id, e.emp_name, d.dept_name, e.salary

FROM employees e

INNER JOIN departments d ON e.dept_id = d.dept_id;

```

2. 左连接 (LEFT JOIN / LEFT OUTER JOIN)

返回左表所有行 + 右表匹配的行(右表无匹配则显示NULL)

```sql

-- 查询所有员工及其部门信息(包括无部门的员工)

SELECT e.*, d.dept_name

FROM employees e

LEFT JOIN departments d ON e.dept_id = d.dept_id;

-- 查找没有部门的员工

SELECT e.*

FROM employees e

LEFT JOIN departments d ON e.dept_id = d.dept_id

WHERE d.dept_id IS NULL;

```

3. 右连接 (RIGHT JOIN / RIGHT OUTER JOIN)

返回右表所有行 + 左表匹配的行

```sql

-- 查询所有部门及其员工(包括无员工的部门)

SELECT d.dept_name, e.emp_name

FROM employees e

RIGHT JOIN departments d ON e.dept_id = d.dept_id;

-- 查找没有员工的部门

SELECT d.*

FROM employees e

RIGHT JOIN departments d ON e.dept_id = d.dept_id

WHERE e.emp_id IS NULL;

```

4. 全外连接 (FULL OUTER JOIN)

MySQL不直接支持,但可以通过UNION模拟

```sql

-- 获取两个表的所有记录(全集)

SELECT e.emp_id, e.emp_name, d.dept_name

FROM employees e

LEFT JOIN departments d ON e.dept_id = d.dept_id

UNION

SELECT e.emp_id, e.emp_name, d.dept_name

FROM employees e

RIGHT JOIN departments d ON e.dept_id = d.dept_id;

```

二、多表关联查询

1. 三表关联示例

```sql

-- 员工-部门-城市三级关联

SELECT

e.emp_id,

e.emp_name,

d.dept_name,

c.city_name,

e.salary

FROM employees e

INNER JOIN departments d ON e.dept_id = d.dept_id

INNER JOIN cities c ON d.city_id = c.city_id

ORDER BY e.salary DESC;

```

2. 自连接 (SELF JOIN)

表与自身关联

```sql

-- 查询每个员工及其经理信息

SELECT

e1.emp_name AS '员工姓名',

e1.position AS '职位',

e2.emp_name AS '经理姓名',

e2.position AS '经理职位'

FROM employees e1

LEFT JOIN employees e2 ON e1.manager_id = e2.emp_id;

-- 查找同一部门的员工对

SELECT

a.emp_name AS '员工A',

b.emp_name AS '员工B',

d.dept_name

FROM employees a

INNER JOIN employees b ON a.dept_id = b.dept_id

INNER JOIN departments d ON a.dept_id = d.dept_id

WHERE a.emp_id < b.emp_id; -- 避免重复组合

```

三、关联查询中的高级技巧

1. 使用别名提高可读性

```sql

SELECT

o.order_id,

o.order_date,

c.customer_name,

p.product_name,

od.quantity,

od.unit_price,

(od.quantity * od.unit_price) AS total_amount

FROM orders o

JOIN customers c ON o.customer_id = c.customer_id

JOIN order_details od ON o.order_id = od.order_id

JOIN products p ON od.product_id = p.product_id

WHERE o.order_date >= '2024-01-01';

```

在线练习mysql(MySQL 表关联查询完全指南)

2. 多条件关联

```sql

-- 多个关联条件

SELECT

s.student_name,

c.course_name,

sc.score,

t.teacher_name

FROM students s

JOIN student_courses sc ON s.student_id = sc.student_id

AND sc.semester = '2024-春季' -- 附加条件

JOIN courses c ON sc.course_id = c.course_id

JOIN teachers t ON c.teacher_id = t.teacher_id

WHERE sc.score >= 60;

```

3. 使用 USING 简化语法(当字段名相同时)

```sql

-- 假设两个表都有相同的字段名 dept_id

SELECT e.emp_name, d.dept_name

FROM employees e

JOIN departments d USING(dept_id); -- 等价于 ON e.dept_id = d.dept_id

```

四、关联查询与聚合函数结合

1. 分组统计

```sql

-- 统计每个部门的员工数量和平均工资

SELECT

d.dept_name,

COUNT(e.emp_id) AS employee_count,

AVG(e.salary) AS avg_salary,

MAX(e.salary) AS max_salary,

MIN(e.salary) AS min_salary,

SUM(e.salary) AS total_salary

FROM departments d

LEFT JOIN employees e ON d.dept_id = e.dept_id

GROUP BY d.dept_id, d.dept_name

HAVING COUNT(e.emp_id) > 0 -- 只显示有员工的部门

ORDER BY avg_salary DESC;

```

2. 子查询与关联查询结合

```sql

-- 查询工资高于部门平均工资的员工

SELECT

e1.emp_name,

e1.salary,

d.dept_name,

(SELECT AVG(e2.salary)

FROM employees e2

WHERE e2.dept_id = e1.dept_id) AS dept_avg_salary

FROM employees e1

JOIN departments d ON e1.dept_id = d.dept_id

WHERE e1.salary > (

SELECT AVG(e3.salary)

FROM employees e3

WHERE e3.dept_id = e1.dept_id

);

```

五、性能优化技巧

1. 创建索引加速关联查询

```sql

-- 为关联字段创建索引

CREATE INDEX idx_dept_id ON employees(dept_id);

CREATE INDEX idx_customer_id ON orders(customer_id);

CREATE INDEX idx_product_id ON order_details(product_id);

-- 复合索引优化多字段查询

CREATE INDEX idx_name_dept ON employees(last_name, first_name, dept_id);

```

2. 使用 EXPLAIN 分析查询性能

```sql

EXPLAIN SELECT e.*, d.dept_name

FROM employees e

JOIN departments d ON e.dept_id = d.dept_id

WHERE e.salary > 5000;

-- 查看执行计划,关注:

-- type: 最好达到 const、eq_ref、ref

-- key: 使用的索引

-- rows: 扫描的行数

-- Extra: 避免出现 "Using filesort" 或 "Using temporary"

```

3. 优化关联顺序

```sql

-- MySQL 通常会自动优化,但可以手动调整

SELECT /*+ STRAIGHT_JOIN */ e.*, d.*

FROM departments d

JOIN employees e ON d.dept_id = e.dept_id

WHERE d.dept_id IN (1, 2, 3);

-- STRAIGHT_JOIN 强制按书写顺序执行连接

```

六、实用场景示例

场景1:电商系统订单查询

```sql

-- 查询客户订单详情

SELECT

c.customer_id,

c.customer_name,

c.email,

o.order_id,

o.order_date,

o.status,

p.product_name,

od.quantity,

od.unit_price,

(od.quantity * od.unit_price) AS item_total,

o.total_amount,

a.address_line,

a.city,

a.postal_code

FROM customers c

JOIN orders o ON c.customer_id = o.customer_id

JOIN order_details od ON o.order_id = od.order_id

JOIN products p ON od.product_id = p.product_id

LEFT JOIN addresses a ON o.shipping_address_id = a.address_id

WHERE c.customer_id = 12345

ORDER BY o.order_date DESC, o.order_id;

```

场景2:员工绩效考核关联

```sql

-- 查询员工绩效及关联信息

SELECT

e.emp_id,

CONCAT(e.last_name, e.first_name) AS full_name,

d.dept_name,

p.position_name,

m.last_name AS manager_name,

kpi.performance_score,

kpi.evaluation_date,

CASE

WHEN kpi.performance_score >= 90 THEN 'A'

WHEN kpi.performance_score >= 80 THEN 'B'

WHEN kpi.performance_score >= 70 THEN 'C'

ELSE 'D'

END AS performance_level,

s.base_salary,

s.bonus

FROM employees e

JOIN departments d ON e.dept_id = d.dept_id

JOIN positions p ON e.position_id = p.position_id

LEFT JOIN employees m ON e.manager_id = m.emp_id

LEFT JOIN performance_kpi kpi ON e.emp_id = kpi.emp_id

AND kpi.evaluation_year = YEAR(CURDATE())

LEFT JOIN salaries s ON e.emp_id = s.emp_id

AND s.effective_date = (

SELECT MAX(effective_date)

FROM salaries

WHERE emp_id = e.emp_id

)

WHERE e.status = '在职'

ORDER BY d.dept_id, kpi.performance_score DESC;

```

七、常见问题与解决方案

问题1:笛卡尔积问题

```sql

-- 错误:忘记关联条件,导致笛卡尔积

SELECT * FROM employees, departments; -- 会产生 M×N 条记录

-- 正确:必须指定关联条件

SELECT * FROM employees e, departments d

WHERE e.dept_id = d.dept_id;

-- 或使用显式 JOIN

SELECT * FROM employees e JOIN departments d ON e.dept_id = d.dept_id;

```

问题2:NULL 值处理

```sql

-- 使用 COALESCE 处理可能的 NULL 值

SELECT

e.emp_name,

COALESCE(d.dept_name, '未分配部门') AS department,

COALESCE(m.emp_name, '无上级') AS manager

FROM employees e

LEFT JOIN departments d ON e.dept_id = d.dept_id

LEFT JOIN employees m ON e.manager_id = m.emp_id;

```

问题3:重复数据去重

```sql

-- 使用 DISTINCT 或 GROUP BY 去重

SELECT DISTINCT d.dept_id, d.dept_name

FROM departments d

JOIN employees e ON d.dept_id = e.dept_id

WHERE e.salary > 8000;

-- 或使用 GROUP BY

SELECT d.dept_id, d.dept_name

FROM departments d

JOIN employees e ON d.dept_id = e.dept_id

WHERE e.salary > 8000

GROUP BY d.dept_id, d.dept_name;

```

八、最佳实践总结

1. 明确关联类型:根据业务需求选择合适的 JOIN 类型

2. 使用表别名:提高可读性,特别是多表关联时

3. 创建必要索引:在关联字段上创建索引,大幅提升查询性能

4. **避免 SELECT ***:只选择需要的字段,减少数据传输

5. 注意 NULL 值:外连接时注意 NULL 值的处理

6. 测试查询性能:使用 EXPLAIN 分析执行计划

7. 分页处理大数据:LIMIT 分页避免一次性返回过多数据

8. 定期优化表:使用 ANALYZE TABLE 更新统计信息

九、复杂关联查询示例

```sql

-- 多层级关联:查询组织架构树

WITH RECURSIVE org_tree AS (

-- 锚点:顶级部门(没有父部门)

SELECT

dept_id,

dept_name,

parent_dept_id,

1 AS level,

CAST(dept_name AS CHAR(1000)) AS path

FROM departments

WHERE parent_dept_id IS NULL


UNION ALL


-- 递归:下级部门

SELECT

d.dept_id,

d.dept_name,

d.parent_dept_id,

ot.level + 1,

CONCAT(ot.path, ' > ', d.dept_name)

FROM departments d

INNER JOIN org_tree ot ON d.parent_dept_id = ot.dept_id

)

SELECT

ot.*,

COUNT(e.emp_id) AS employee_count

FROM org_tree ot

LEFT JOIN employees e ON ot.dept_id = e.dept_id

GROUP BY ot.dept_id, ot.dept_name, ot.level, ot.path

ORDER BY ot.path;

```

通过掌握这些 MySQL 表关联查询技巧,您可以高效地处理各种复杂的数据关系查询需求。在实际应用中,根据数据量大小、查询频率和业务需求,选择合适的关联方式和优化策略。

文章版权声明:除非注明,否则均为边学边练网络文章,版权归原作者所有

相关阅读