You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何编写SQL查询统计过去12个月每月分数总和,无数据月份补0用于Chart.js

解决方案

原代码问题梳理

  • SQL语法错误:子查询嵌套了非法的SUM查询、日期字段名写错(表中实际为date_created,代码错写为date_added/date)、多处引号缺失
  • 逻辑缺陷:没有生成连续月份序列,无法为无数据的月份自动补0
  • 安全风险:直接拼接用户ID到SQL语句,存在SQL注入漏洞
  • 结果处理错误:直接输出查询结果对象,没有转换为可读取的数组格式

完整实现代码

function get_last_12_month_scores(){
    $db = Database::getInstance();
    $mysqli = $db->getConnection();
    $user_id = $_SESSION['user_id'] ?? 0;
    $result = [];

    // 生成过去12个月连续序列 + 关联分数求和的SQL,支持无数据补0
    $sql = "
WITH RECURSIVE months AS (
    -- 取11个月前的第一天作为起始点,覆盖完整12个月周期
    SELECT DATE_FORMAT(DATE_SUB(NOW(), INTERVAL 11 MONTH), '%Y-%m-01') as month_date
    UNION ALL
    SELECT DATE_ADD(month_date, INTERVAL 1 MONTH)
    FROM months
    WHERE month_date < DATE_FORMAT(NOW(), '%Y-%m-01')
)
SELECT 
    DATE_FORMAT(m.month_date, '%M') as month_name,
    IFNULL(SUM(s.marks), 0) as total_marks
FROM months m
LEFT JOIN scores s 
    ON DATE_FORMAT(s.date_created, '%Y-%m') = DATE_FORMAT(m.month_date, '%Y-%m')
    AND s.user_id = ?
GROUP BY m.month_date
-- 倒序排列实现最新月份排在结果最前,最旧月份在最后,匹配需求输出顺序
ORDER BY m.month_date DESC
";

    // 用预处理语句防SQL注入
    $stmt = $mysqli->prepare($sql);
    $stmt->bind_param("i", $user_id);
    $stmt->execute();
    $query_result = $stmt->get_result();
    
    // 转换为索引数组,直接支持[i]下标读取
    while($row = $query_result->fetch_assoc()){
        $result[] = $row['total_marks'];
        // 若需要同时返回月份名供Chart.js标签使用,可改成 $result[] = ['month' => $row['month_name'], 'value' => $row['total_marks']];
    }
    $stmt->close();
    return $result;
}

使用说明

  • 调用get_last_12_month_scores()即可获得长度为12的索引数组,数组顺序和你要求的输出完全一致,每个元素对应当月分数总和
  • 若使用的是不支持CTE的MySQL 5.x版本,可替换递归CTE部分为临时表生成连续月份的逻辑即可

内容的提问来源于stack exchange,提问作者N Jedidiah

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 03:39:00