去重应由SQL控制而非PHP,优先用SELECT DISTINCT或GROUP BY;DISTINCT作用于整行,NULL视为相同;GROUP BY更可控且支持聚合规则;索引对去重性能至关重要。

PHP 执行 SQL 查询时,去重逻辑不该放在 PHP 层做,而应由 SQL 本身控制结果集。直接在 SELECT 中加 DISTINCT 或用 GROUP BY 是正确路径;用 array_unique() 处理全量结果是典型性能陷阱。
SELECT DISTINCT 要作用于整行,不是单列
DISTINCT 判断重复的依据是 SELECT 后所有字段的组合值完全一致。哪怕只多一个字段,就可能让本该去重的数据“逃逸”。
- 想取不重复的邮箱:
SELECT DISTINCT email FROM users - 错误写法:
SELECT DISTINCT email, name FROM users—— 这会保留所有email+name组合不同的行,哪怕邮箱相同但名字不同也会全出来 -
NULL被视为相同值,SELECT DISTINCT col FROM t中多个NULL只返回一行 - MySQL 8.0+ 严格模式下,
ORDER BY字段必须出现在SELECT列表里,否则报错ERROR 1055
GROUP BY 比 DISTINCT 更可控,尤其要留最新/最小 ID 时
当你要去重,同时还得知道“每组留哪一条”,GROUP BY 配合聚合函数才是可靠选择。它天然支持业务规则,DISTINCT 做不到。
- 按
email去重并保留最新记录:SELECT * FROM users WHERE id IN (SELECT MAX(id) FROM users GROUP BY email) - 若需返回完整字段又不想写子查询,可用
JOIN+GROUP BY衍生表:SELECT u.* FROM users u JOIN (SELECT email, MAX(id) AS max_id FROM users GROUP BY email) t ON u.id = t.max_id - 注意:MySQL 5.7+ 默认开启
ONLY_FULL_GROUP_BY,SELECT *+GROUP BY email会报错,必须显式指定非分组字段的聚合方式
ThinkPHP 等 ORM 中的 distinct() 和 group() 不是语法糖,行为有差异
框架封装的去重方法背后仍是 SQL,但调用方式影响生成语句的结构和可维护性。
-
->distinct(true)->field('email')生成的是SELECT DISTINCT email ...,安全、直观 -
->group('email')生成的是GROUP BY email,但若没配SELECT字段或聚合,MySQL 8.0+ 会拒绝执行 - 别混用:
->distinct(true)->group('email')在多数框架里无效,甚至触发异常 ——DISTINCT和GROUP BY语义冲突,数据库不允许共存 - 复杂场景(如按 email 去重但留 update_time 最大的那条),框架原生方法不够用,得手写子查询或 CTE
array_unique() 处理查询结果 = 主动放弃数据库能力
除非你明确知道自己在合并多个来源的数据,否则不要把全量数据拉到 PHP 再去重。这等于绕开索引、绕开排序优化、绕开数据库的并行能力。
- 查 10 万行只为了展示 200 个唯一城市?
SELECT DISTINCT city FROM users几毫秒搞定;用array_unique()至少多占 100MB 内存、延迟翻倍 -
array_unique()对关联数组默认只比「值」,['id'=>1,'name'=>'a']和['id'=>2,'name'=>'a']会被当成相同值丢掉一个 —— 因为它把整个子数组当 value 比较,行为不可控 - 真要 PHP 层后处理,请构造唯一键:
$key = $row['email'] . '|' . $row['phone'],再用键名去重,比array_unique()稳定得多
最常被忽略的一点:判重字段是否建了索引。没索引的 GROUP BY email 或 DISTINCT email 在百万级表上可能跑出 30 秒,加个联合索引 INDEX(email) 就降到 0.1 秒 —— 去重效率不只看写法,更取决于底层数据组织。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











