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

Laravel Eloquent高效删除MySQL重复行 保留每组首条记录

大表重复数据删除高效实现

你当前使用的循环逐组查询删除方案属于典型的N+1查询场景,在千万级数据量下会产生成千上万次SQL请求,性能极差。以下两种方案均可单次定位所有待删除的重复记录ID,配合分块删除可以把执行时间从小时级压缩到分钟级。

重复判定规则保持不变:codes和customer_id字段值同时相同即为重复,每组保留ID最小的第一条记录。


方案一:MySQL 8.0+ 窗口函数方案(优先推荐)

基于ROW_NUMBER()窗口函数实现分组排序,直接标记每组内除首条(ID最小)之外的所有重复记录,没有多余关联查询,大表下性能最优。

// 构建待删除重复记录的查询,所有行号>1的都是需要删除的非首条记录
$duplicateList = Order::select('id')
    ->fromRaw('(
        SELECT id,
        ROW_NUMBER() OVER (PARTITION BY codes, customer_id ORDER BY id ASC) AS row_num
        FROM orders
    ) AS temp')
    ->where('row_num', '>', 1);

// 分块删除,每次处理1000条,避免长事务、锁表和主从延迟
$duplicateList->chunkById(1000, function ($duplicates) {
    $deleteIds = $duplicates->pluck('id')->toArray();
    Order::whereIn('id', $deleteIds)->delete();
});

方案二:MySQL 5.7 兼容方案

如果你的数据库版本低于8.0不支持窗口函数,使用自关联查询标记重复项,逻辑为:同codes+customer_id分组下,只要存在ID比当前记录更小的条目,当前记录就是需要删除的重复项。

$duplicateList = Order::selectRaw('DISTINCT t1.id')
    ->from('orders AS t1')
    ->join('orders AS t2', function ($join) {
        $join->on('t1.codes', '=', 't2.codes')
            ->on('t1.customer_id', '=', 't2.customer_id')
            ->on('t1.id', '>', 't2.id');
    });

// 同样采用分块删除逻辑
$duplicateList->chunkById(1000, function ($duplicates) {
    $deleteIds = $duplicates->pluck('id')->toArray();
    Order::whereIn('id', $deleteIds)->delete();
});

必做优化项

  • 执行删除前先给两个判定字段加联合索引,查询性能可提升100倍以上:
    ALTER TABLE orders ADD INDEX idx_codes_customer (codes, customer_id);
    
  • 分块大小根据自身数据库负载调整,建议控制在500-2000条区间,不要一次性删除超大量数据
  • 正式执行前建议先查询待删除数据做备份,避免误删:
    // 备份示例
    // $backupData = $duplicateList->get();
    // 存储到本地文件即可
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 12:15:44