MySQL 多表关联查询与聚合运算实战技巧
2024-12-20 00:43:40
MySQL 多表关联查询与聚合运算实战
多表关联查询及聚合运算是数据库应用的常见需求,有效运用可提高数据处理效率。面临多表数据整合与计算,构建合理的 SQL 语句是核心。例如用户数据分布在不同的表里(users
、lessons
、jobs
、offers
),为了得到统计结果, 进行排序展示。下面将探讨这个问题,分析其中的要点并提供具体方案。
场景及难点分析
用户(users
)、课程(lessons
)、工作(jobs
)、工作邀请(offers
)信息存储在各自独立的表里,且用户表记录着用户的关键数据(姓名)。每个课程记录着总费用(totalPay
),jobs
表和offers
表仅作简单计数即可,如果相应的数据行不存在, 返回 0,并且计算结果要用用户表的其它字段呈现。lessons
表做 SUM 操作,并且表间关系为:users
表通过usersid
分别与其余三张表关联,进行 LEFT JOIN,保证无论其余三张表有无数据, 都能返回users
表所有的数据。 统计得到课程费用总额 (totalSum
)、jobs
表数据总数(greens
)、offers
表数据总数(golds
)后需要做如下运算并对结果排序:
user_score = 0;
if (greens>0 && golds>0){
user_score = (greens/golds) * totalSum;
user_score = round(user_score);
}
最后返回usersid
,user_score
和用户的姓名及其他必要信息,以user_score
降序排列。这里关键在于合理构建 JOIN 语句、运用聚合函数(SUM, COUNT)、正确处理 NULL 值(赋予 0 值)以及公式计算,最后以计算得出的user_score
作为排序依据,整合出最终结果。
方案一: 分步查询与计算
思路:按步骤拆解需求,针对各个表先进行聚合查询,随后汇总计算。
步骤:
- 单独查询
lessons
表,得到每个用户的totalSum
。 - 单独查询
jobs
表,得到每个用户的greens
。 - 单独查询
offers
表,得到每个用户的golds
。 - 以
users
表为主表,将上述三个查询结果通过usersid
关联,使用COALESCE
函数处理空值为0。 - 应用计算公式计算
user_score
。 - 根据
user_score
排序。
-- Step 1: 查询 lessons 表
CREATE TEMPORARY TABLE LessonSummary AS
SELECT usersid, COALESCE(SUM(totalPay), 0) AS totalSum
FROM lessons
GROUP BY usersid;
-- Step 2: 查询 jobs 表
CREATE TEMPORARY TABLE JobSummary AS
SELECT usersid, COUNT(jobsid) AS greens
FROM jobs
GROUP BY usersid;
-- Step 3: 查询 offers 表
CREATE TEMPORARY TABLE OfferSummary AS
SELECT usersid, COUNT(offersid) AS golds
FROM offers
GROUP BY usersid;
-- Step 4 & 5 & 6: 关联查询并计算 user_score
SELECT
u.usersid,
u.name,
COALESCE(ls.totalSum, 0) AS totalSum,
COALESCE(js.greens, 0) AS greens,
COALESCE(os.golds, 0) AS golds,
CASE
WHEN COALESCE(js.greens, 0) > 0 AND COALESCE(os.golds, 0) > 0 THEN ROUND((COALESCE(js.greens, 0) / COALESCE(os.golds, 0)) * COALESCE(ls.totalSum, 0))
ELSE 0
END AS user_score
FROM users u
LEFT JOIN LessonSummary ls ON u.usersid = ls.usersid
LEFT JOIN JobSummary js ON u.usersid = js.usersid
LEFT JOIN OfferSummary os ON u.usersid = os.usersid
ORDER BY user_score DESC;
使用临时表的好处在于可以清晰看到数据的流转步骤。不过使用临时表也可能会在一定程度上增加服务器开销。如果业务环境比较在意单次执行速度,可以参照方案二,直接对子查询结果进行组合和计算。
方案二: 子查询与公式计算
思路:将每个表的聚合结果作为子查询,与users
表关联后,统一进行计算。
步骤:
users
表分别与lessons
、jobs
、offers
表的子查询结果进行 LEFT JOIN。- 子查询里分别得到每个表的统计值,并且使用
COALESCE
把统计结果为 NULL 的变为 0。 - JOIN 之后, 应用计算公式得到
user_score
。 - 根据
user_score
对整个结果排序。
SELECT
u.usersid,
u.name,
COALESCE(ls.totalSum, 0) AS totalSum,
COALESCE(js.greens, 0) AS greens,
COALESCE(os.golds, 0) AS golds,
CASE
WHEN COALESCE(js.greens, 0) > 0 AND COALESCE(os.golds, 0) > 0 THEN ROUND((COALESCE(js.greens, 0) / COALESCE(os.golds, 0)) * COALESCE(ls.totalSum, 0))
ELSE 0
END AS user_score
FROM
users u
LEFT JOIN
(SELECT usersid, SUM(totalPay) AS totalSum FROM lessons GROUP BY usersid) ls ON u.usersid = ls.usersid
LEFT JOIN
(SELECT usersid, COUNT(jobsid) AS greens FROM jobs GROUP BY usersid) js ON u.usersid = js.usersid
LEFT JOIN
(SELECT usersid, COUNT(offersid) AS golds FROM offers GROUP BY usersid) os ON u.usersid = os.usersid
ORDER BY
user_score DESC;
这个方案比上一个方案执行起来效率高。在实际选择时需要注意, MySQL对复杂 SQL 语句的优化不一定有预期好, 具体效果如何还得进行基准测试后才好定论。
安全建议
防范 SQL 注入是重中之重, 杜绝手动拼接 SQL 语句。处理用户输入时要进行输入验证,或者使用参数化查询。生产环境不要轻易调整表结构或者修改运行中系统的配置,表结构修改需要做好备份,调整配置应该尽量避免高峰时间段, 必要情况下需要回滚预案。执行权限的管理也必不可少。不要用太高级别的用户运行后台程序,用户要设置强口令。