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

Laravel Eloquent:一对多关联表按时间分组查询交易及总手续费

嘿,我来帮你搞定这个分组查询的问题!你提到的transactions和transaction_charges一对多关联,完全可以直接在查询语句里完成分组+总手续费计算,不用事后遍历模型集合折腾。下面分原生SQL和常见ORM(比如Laravel Eloquent)两种场景给你具体方案:

一、原生SQL实现方案

不管你用哪种数据库,核心思路都是关联两张表,用日期函数提取时间维度(月/年/周),再按这个维度分组,最后聚合计算总手续费和交易数量。

1. 月度统计

SELECT
    DATE_FORMAT(t.created_at, '%Y-%m') AS month, -- 格式化为「年-月」,比如2024-05
    COUNT(t.id) AS transaction_count, -- 该月交易总数
    SUM(tc.amount) AS total_charges -- 该月总手续费
FROM transactions t
LEFT JOIN transaction_charges tc ON t.id = tc.transaction_id
GROUP BY DATE_FORMAT(t.created_at, '%Y-%m')
ORDER BY month DESC; -- 按时间倒序排列

小贴士:用LEFT JOIN是为了保证没有手续费的交易也会被统计,如果用INNER JOIN会过滤掉这类交易,按需选择。

2. 年度统计

SELECT
    YEAR(t.created_at) AS year, -- 提取年份,比如2024
    COUNT(t.id) AS transaction_count,
    SUM(tc.amount) AS total_charges
FROM transactions t
LEFT JOIN transaction_charges tc ON t.id = tc.transaction_id
GROUP BY YEAR(t.created_at)
ORDER BY year DESC;

3. 周度统计

不同数据库的周函数略有差异,这里分两种常用情况:

MySQL/MariaDB

SELECT
    DATE_FORMAT(t.created_at, '%Y-%u') AS week, -- %u表示周一为一周起始,%v表示周日为起始,按需切换
    COUNT(t.id) AS transaction_count,
    SUM(tc.amount) AS total_charges
FROM transactions t
LEFT JOIN transaction_charges tc ON t.id = tc.transaction_id
GROUP BY DATE_FORMAT(t.created_at, '%Y-%u')
ORDER BY week DESC;

PostgreSQL

SELECT
    DATE_TRUNC('week', t.created_at) AS week_start, -- 返回该周的起始日期,比如2024-05-20
    COUNT(t.id) AS transaction_count,
    SUM(tc.amount) AS total_charges
FROM transactions t
LEFT JOIN transaction_charges tc ON t.id = tc.transaction_id
GROUP BY DATE_TRUNC('week', t.created_at)
ORDER BY week_start DESC;
二、Laravel Eloquent ORM实现方案

如果你用的是Laravel(毕竟你提到了「模型集合」),可以用ORM优雅实现,先确保模型关联正确:

// app/Models/Transaction.php
public function charges()
{
    return $this->hasMany(TransactionCharge::class);
}

1. 月度统计

use Illuminate\Support\Facades\DB;

$monthlyStats = Transaction::query()
    ->select([
        DB::raw("DATE_FORMAT(created_at, '%Y-%m') as month"),
        DB::raw('COUNT(id) as transaction_count'),
        DB::raw('SUM(transaction_charges.amount) as total_charges')
    ])
    ->leftJoin('transaction_charges', 'transactions.id', '=', 'transaction_charges.transaction_id')
    ->groupBy('month')
    ->orderBy('month', 'desc')
    ->get();

2. 年度统计

$yearlyStats = Transaction::query()
    ->select([
        DB::raw("YEAR(created_at) as year"),
        DB::raw('COUNT(id) as transaction_count'),
        DB::raw('SUM(transaction_charges.amount) as total_charges')
    ])
    ->leftJoin('transaction_charges', 'transactions.id', '=', 'transaction_charges.transaction_id')
    ->groupBy('year')
    ->orderBy('year', 'desc')
    ->get();

3. 周度统计

MySQL/MariaDB版本

$weeklyStats = Transaction::query()
    ->select([
        DB::raw("DATE_FORMAT(created_at, '%Y-%u') as week"),
        DB::raw('COUNT(id) as transaction_count'),
        DB::raw('SUM(transaction_charges.amount) as total_charges')
    ])
    ->leftJoin('transaction_charges', 'transactions.id', '=', 'transaction_charges.transaction_id')
    ->groupBy('week')
    ->orderBy('week', 'desc')
    ->get();

PostgreSQL版本

$weeklyStats = Transaction::query()
    ->select([
        DB::raw("DATE_TRUNC('week', created_at) as week_start"),
        DB::raw('COUNT(id) as transaction_count'),
        DB::raw('SUM(transaction_charges.amount) as total_charges')
    ])
    ->leftJoin('transaction_charges', 'transactions.id', '=', 'transaction_charges.transaction_id')
    ->groupBy('week_start')
    ->orderBy('week_start', 'desc')
    ->get();
几个关键注意点
  • 如果你开启了MySQL的ONLY_FULL_GROUP_BY严格模式,确保SELECT里的非聚合列都在GROUP BY中,上面的写法已经满足这个要求;
  • 如果交易表用了软删除,记得在查询中加上->withTrashed()(如果需要包含已删除交易);
  • 要是你需要按其他时间维度(比如季度),只需要替换对应的日期函数即可,逻辑完全一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 09:10:44