应使用关联子查询或窗口函数;min()或order by...limit 1无法保证返回对应行的完整字段,如最早时间对应的姓名、id等。

子查询怎么写才能拿到每组第一条?
直接用 MIN() 或 ORDER BY ... LIMIT 1 在子查询里是错的——它们不保证返回对应行的完整字段,比如时间最早但姓名、ID 拿不到。真正能拿到整行数据的,得靠关联子查询或窗口函数,而多数老版本 MySQL(
MySQL 5.7 怎么用关联子查询取每组最早记录?
核心思路:对每条记录,查出同组中是否存在更早的记录;如果不存在,它就是最早的。用 NOT EXISTS 最稳,比 = (SELECT MIN(...)) 更安全(避免多条同时间时重复返回)。
- 假设表叫
orders,字段有user_id、created_at、order_id - 子查询要和外层共享分组字段:
WHERE o2.user_id = o1.user_id AND o2.created_at - 必须给
created_at加索引,否则性能崩得很快
SELECT o1.*
FROM orders o1
WHERE NOT EXISTS (
SELECT 1 FROM orders o2
WHERE o2.user_id = o1.user_id
AND o2.created_at <h3>PostgreSQL / MySQL 8.0+ 用窗口函数更简单</h3><p><code>ROW_NUMBER()</code> 是最直观的选择,但要注意排序字段必须唯一或加足够粒度的二级排序,否则 <code>ROW_NUMBER()</code> 和 <code>RANK()</code> 行为不同——前者强制编号,后者会并列跳号。</p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/ai/867" title="Rezi.ai"><img
src="https://img.php.cn/upload/ai_manual/001/503/042/68b6d60644bb7461.png" alt="Rezi.ai" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/ai/867" title="Rezi.ai" class="overflowclass">Rezi.ai</a>
<p class="overflowclass">Rezi.ai是一款用于创建 ATS 友好简历、求职信和求职材料的 AI 简历工具。</p>
</div>
<a rel="nofollow" href="/ai/867" title="Rezi.ai" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div>
- 用
PARTITION BY user_id ORDER BY created_at, id避免时间相同时结果不确定 -
ROW_NUMBER() OVER (...) = 1筛出每组首行,比子查询快且可读性强 - 别漏写
OVER的括号,语法错误常出在这里
SELECT * FROM (
SELECT *, ROW_NUMBER() OVER (
PARTITION BY user_id ORDER BY created_at, id
) rn
FROM orders
) t WHERE rn = 1;
为什么 GROUP BY + MIN(created_at) 不行?
这是最常见误区:SELECT user_id, MIN(created_at), order_id FROM orders GROUP BY user_id —— order_id 值是随机的,不是对应最早时间那条的 order_id。MySQL 5.7 默认开启 ONLY_FULL_GROUP_BY,这种写法直接报错:Expression #3 of SELECT list is not in GROUP BY clause。
- 即使关掉严格模式,返回的
order_id也不可靠,引擎随便挑一条 - 想混用聚合和非聚合字段,必须用子查询或窗口函数兜底
- 有些 ORM 自动生成这类 SQL,上线前务必检查执行计划和实际结果
真实业务里,时间字段精度、空值、时区偏移都会影响“最早”的判断,别只盯着语法——先确认 created_at 是否有 NULL,是否统一了时区,再决定要不要加 WHERE created_at IS NOT NULL。










