
本文详解如何在 CodeIgniter 中为数组中的每个用户 ID 独立获取指定数量(如最近 10 条)的交易记录,突破 WHERE_IN + 全局 LIMIT 的限制,借助 MySQL 变量模拟窗口函数实现分组限行。
本文详解如何在 codeigniter 中为数组中的每个用户 id 独立获取指定数量(如最近 10 条)的交易记录,突破 `where_in` + 全局 `limit` 的限制,借助 mysql 变量模拟窗口函数实现分组限行。
在 CodeIgniter 开发中,一个常见但原生 Query Builder 不直接支持的需求是:对 WHERE IN 匹配的多个 ID,分别为每个 ID 获取其最新的 N 条关联记录(例如“为传入的 5 个用户 ID,各取最近 10 笔交易”)。标准的 $this->db->limit(10) 仅作用于最终结果集整体,无法满足“每组独立限行”的语义。本文提供稳定、可落地的解决方案。
✅ 核心思路:用 MySQL 用户变量模拟 ROW_NUMBER() OVER (PARTITION BY ... ORDER BY ...)
由于旧版 CodeIgniter(v3.x)不原生支持窗口函数(MySQL 8.0+ 才全面支持),我们采用兼容性更强的变量计数法:
- 利用 CROSS JOIN 初始化会话级变量 @sameClass(记录上一个 userId)和 @rn(当前序号);
- 按 userId, id 排序后遍历,当 userId 不变时递增 @rn,切换 userId 时重置为 1;
- 外层筛选 rank
✅ 完整可运行模型方法
function getTrans($cData, $date = 0) {
// ✅ 安全处理输入:确保 $cData 是非空数组,且所有元素为整型
if (!is_array($cData) || empty($cData)) {
return false;
}
$cData = array_map('intval', $cData); // 强制转整型,防注入基础防护
$ids_str = implode(', ', $cData);
// ✅ 构建安全 WHERE 条件(日期参数需手动转义)
$date_condition = ($date != 0) ? "AND date >= " . $this->db->escape($date) : "";
$sql = "
SELECT id, userId, date, amount, type -- ⚠️ 建议显式列出字段,避免 SELECT *
FROM (
SELECT
trans.*,
IF(@sameClass = userId, @rn := @rn + 1,
IF(@sameClass := userId, @rn := 1, @rn := 1)
) AS rank
FROM trans
CROSS JOIN (SELECT @sameClass := 0, @rn := 1) AS var
WHERE userId IN ({$ids_str}) {$date_condition}
ORDER BY userId, id DESC -- ? 关键:按 userId 分组,id 降序 → 最新在前
) AS t
WHERE t.rank <= 10
ORDER BY t.userId, t.rank;
";
$query = $this->db->query($sql);
return $query->num_rows() > 0 ? $query->result() : [];
}⚠️ 重要注意事项
- SQL 注入防护:本例中 userId 数组经 intval() 过滤,$date 使用 $this->db->escape() 处理。切勿直接拼接未过滤的用户输入!
- 排序方向决定“最新”含义:ORDER BY userId, id DESC 确保 rank=1 是每个用户的最大 id(通常对应最新记录);若时间字段更准确,应改为 ORDER BY userId, date DESC 并确保 date 有索引。
-
性能优化建议:
- 为 (userId, date) 或 (userId, id) 建立复合索引,大幅提升子查询排序效率;
- 避免在大数据表上无 date 条件全量扫描。
-
MySQL 版本兼容性:该方案适用于 MySQL 5.6+;若已升级至 MySQL 8.0+,推荐改用标准窗口函数(更清晰、更高效):
SELECT id, userId, date FROM ( SELECT *, ROW_NUMBER() OVER (PARTITION BY userId ORDER BY id DESC) AS rn FROM trans WHERE userId IN ({$ids_str}) {$date_condition} ) t WHERE t.rn <= 10;
✅ 总结
CodeIgniter 的 Query Builder 虽简洁,但在复杂分组限行场景下需回归原生 SQL。本文提供的变量计数方案,兼顾了兼容性、可读性与实用性。关键在于理解“分组内序号”的生成逻辑,并通过两层嵌套查询精准截断。实际项目中,请务必结合索引优化与输入校验,确保方案健壮高效。










