DELIMITER // CREATEPROCEDURE GetUserCount() BEGIN SELECTCOUNT(*) AS total_users FROM users; END// DELIMITER ;
-- 调用存储过程 CALL GetUserCount();
内连接 (INNER JOIN)
内连接返回两个表中连接字段匹配的行。
1 2 3
SELECT a.id, a.name, b.order_date, b.amount FROM customers a INNERJOIN orders b ON a.id = b.customer_id;
左连接 (LEFT JOIN)
左连接返回左表的所有记录,即使右表中没有匹配。
1 2 3
SELECT a.id, a.name, b.order_date, b.amount FROM customers a LEFTJOIN orders b ON a.id = b.customer_id;
右连接 (RIGHT JOIN)
右连接返回右表的所有记录,即使左表中没有匹配。
1 2 3
SELECT a.id, a.name, b.order_date, b.amount FROM customers a RIGHTJOIN orders b ON a.id = b.customer_id;
全连接 (FULL JOIN)
MySQL 不直接支持 FULL JOIN,但可以通过 UNION 实现:
1 2 3 4 5 6 7 8
SELECT a.id, a.name, b.order_date, b.amount FROM customers a LEFTJOIN orders b ON a.id = b.customer_id UNION SELECT a.id, a.name, b.order_date, b.amount FROM customers a RIGHTJOIN orders b ON a.id = b.customer_id WHERE a.id ISNULL;
交叉连接 (CROSS JOIN)
返回两个表的笛卡尔积。
1 2 3
SELECT a.id, a.name, b.order_date, b.amount FROM customers a CROSSJOIN orders b;
自连接 (SELF JOIN)
表与自身连接。
1 2 3
SELECT a.name AS employee, b.name AS manager FROM employees a LEFTJOIN employees b ON a.manager_id = b.id;
多表查询的复杂示例
三表连接示例
1 2 3 4 5 6 7 8 9 10 11 12
SELECT c.name AS customer_name, p.name AS product_name, o.order_date, oi.quantity, oi.price FROM customers c INNERJOIN orders o ON c.id = o.customer_id INNERJOIN order_items oi ON o.id = oi.order_id INNERJOIN products p ON oi.product_id = p.id WHERE o.order_date >'2023-01-01' ORDERBY o.order_date DESC;
子查询作为表连接
1 2 3 4 5 6 7 8 9
SELECT a.name, b.total_orders FROM customers a LEFTJOIN ( SELECT customer_id, COUNT(*) AS total_orders FROM orders GROUPBY customer_id ) b ON a.id = b.customer_id;
SELECT department_id, AVG(salary) AS avg_salary FROM employees WHERE hire_date >'2020-01-01' GROUPBY department_id HAVINGAVG(salary) >5000 ORDERBY avg_salary DESC LIMIT 10;
实际执行顺序:
FROM employees - 从employees表获取数据
WHERE hire_date > ‘2020-01-01’ - 过滤入职日期
GROUP BY department_id - 按部门分组
HAVING AVG(salary) > 5000 - 过滤平均工资>5000的部门
SELECT department_id, AVG(salary) - 选择要显示的列
ORDER BY avg_salary DESC - 按平均工资降序排序
LIMIT 10 - 只返回前10条记录
备注:8.0以后的with语句
一、基本语法
1 2 3 4
WITH cte_name AS ( SELECT ... -- 定义CTE的查询 ) SELECT*FROM cte_name; -- 使用CTE
二、CTE 的主要类型
非递归 CTE
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
WITH department_stats AS ( SELECT department_id, COUNT(*) AS employee_count, AVG(salary) AS avg_salary FROM employees GROUPBY department_id ) SELECT d.department_name, ds.employee_count, ds.avg_salary FROM departments d JOIN department_stats ds ON d.department_id = ds.department_id ORDERBY ds.avg_salary DESC;
WITHRECURSIVE employee_hierarchy AS ( -- 基础查询(锚成员) SELECT id, name, manager_id, 1AS level FROM employees WHERE manager_id ISNULL UNIONALL -- 递归查询(递归成员) SELECT e.id, e.name, e.manager_id, eh.level +1 FROM employees e JOIN employee_hierarchy eh ON e.manager_id = eh.id ) SELECT*FROM employee_hierarchy ORDERBY level, name;
三、CTE 的执行顺序
在包含 WITH 子句的查询中,执行顺序如下:
首先评估所有 CTE:按照它们在查询中出现的顺序
然后执行主查询:可以引用已定义的 CTE
CTE 只在当前查询中有效:不会影响其他查询
四、多 CTE 示例
可以在一个查询中定义多个 CTE:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
WITH high_salary_employees AS ( SELECT*FROM employees WHERE salary >10000 ), department_budgets AS ( SELECT department_id, SUM(salary) AS total_salary FROM employees GROUPBY department_id ) SELECT d.department_name, COUNT(hse.id) AS high_salary_count, db.total_salary FROM departments d LEFTJOIN high_salary_employees hse ON d.department_id = hse.department_id JOIN department_budgets db ON d.department_id = db.department_id GROUPBY d.department_id, d.department_name, db.total_salary;
WITHRECURSIVE emp_path AS ( -- 基础查询:顶级管理者 SELECT id, name, manager_id, name AS path FROM employees WHERE manager_id ISNULL UNIONALL -- 递归查询:下属员工 SELECT e.id, e.name, e.manager_id, CONCAT(ep.path, ' > ', e.name) AS path FROM employees e JOIN emp_path ep ON e.manager_id = ep.id ) SELECT*FROM emp_path ORDERBY path;