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

MySQL多次连接同一张表并分组时,如何正确统计多时段聚合值?

解决多次连接同一张表统计多时段聚合值的数值错误问题

嗨,这个问题我之前做电商数据统计时也踩过坑!核心问题出在多次左同一张表时产生的笛卡尔积——当你同时连接lastDay、lastMonth和lastYear这三个子数据集时,它们会互相交叉匹配,导致单条统计记录被重复计算N次,最终的SUM结果自然就会虚高。

举个简单例子:如果某商品在lastDay有1440条记录(每分钟1条),lastMonth有43200条记录,连接后就会产生1440*43200条交叉记录,求和时每条unitsSold都会被重复计算成百上千次,数值能对才怪。

下面给你两种实用的解决方案:

方案一:子查询预聚合后再连接

先对每个时段单独做聚合统计,得到每个商品对应时段的总和,再和主表连接。这样每个子查询的结果都是按productId聚合后的单条记录,完全避免笛卡尔积:

SELECT 
    p.id, 
    p.name,
    -- 用COALESCE把NULL转为0,避免无数据商品显示空值
    COALESCE(ld.latDayUnitsSold, 0) AS latDayUnitsSold,
    COALESCE(lm.latMonthUnitsSold, 0) AS latMonthUnitsSold,
    COALESCE(ly.latYearUnitsSold, 0) AS latYearUnitsSold
FROM `products` p
LEFT JOIN (
    SELECT productId, SUM(unitsSold) AS latDayUnitsSold
    FROM `productStats`
    WHERE `time` BETWEEN '2018-05-27 00:00:00' AND '2018-05-28 00:00:00'
    GROUP BY productId
) ld ON p.id = ld.productId
LEFT JOIN (
    SELECT productId, SUM(unitsSold) AS latMonthUnitsSold
    FROM `productStats`
    WHERE `time` BETWEEN '2018-04-28 00:00:00' AND '2018-05-28 00:00:00'
    GROUP BY productId
) lm ON p.id = lm.productId
LEFT JOIN (
    SELECT productId, SUM(unitsSold) AS latYearUnitsSold
    FROM `productStats`
    WHERE `time` BETWEEN '2017-05-28 00:00:00' AND '2018-05-28 00:00:00'
    GROUP BY productId
) ly ON p.id = ly.productId;

方案二:条件聚合(推荐,性能更优)

只连接一次productStats表,用CASE WHEN在SUM里筛选对应时段的数据,这种方法只需要扫描一次统计表格,性能比多次连接好很多:

SELECT 
    p.id, 
    p.name,
    SUM(CASE 
        WHEN ps.`time` BETWEEN '2018-05-27 00:00:00' AND '2018-05-28 00:00:00' 
        THEN ps.unitsSold ELSE 0 
    END) AS latDayUnitsSold,
    SUM(CASE 
        WHEN ps.`time` BETWEEN '2018-04-28 00:00:00' AND '2018-05-28 00:00:00' 
        THEN ps.unitsSold ELSE 0 
    END) AS latMonthUnitsSold,
    SUM(CASE 
        WHEN ps.`time` BETWEEN '2017-05-28 00:00:00' AND '2018-05-28 00:00:00' 
        THEN ps.unitsSold ELSE 0 
    END) AS latYearUnitsSold
FROM `products` p
LEFT JOIN `productStats` ps ON p.id = ps.productId
GROUP BY p.id, p.name;

另外还要提一句:你原来的SQL里有个低级笔误——lastMonth和lastYear的连接条件里用了lastDay.time,应该改成lastMonth.time和lastYear.time,不过即使修正了这个,笛卡尔积的问题依然存在,还是上面两种方案靠谱。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 07:34:33