关于mysql中select出数据未排序问题

本文精选自《MySQL必知必会》,深入浅出地讲解了MySQL数据库的基础知识与核心概念,适合初学者及有一定经验的数据库管理员阅读,旨在帮助读者掌握MySQL的使用技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

 

来自  mysql必知必会

 

 

 

转载于:https://www.cnblogs.com/ellisonzhang/p/10490197.html

<think>我们面对的问题是:在MySQL中,当我们使用DISTINCT时,如果ORDER BY子句引用了不在SELECT列表中的列,就会报错:“Expression #1 of ORDER BY clause is not in SELECT list, references column '...' which is not in SELECT list; this is incompatible with DISTINCT”。 根据引用[1][2][3]的分析,这是因为MySQL 8.0及更高版本默认启用了sql_mode中的ONLY_FULL_GROUP_BY模式,该模式要求在使用DISTINCT时,ORDER BY子句中的列必须在SELECT列表中。 解决方案有以下几种: 1. 将ORDER BY中引用的列添加到SELECT列表中(推荐)。 2. 修改sql_mode,移除ONLY_FULL_GROUP_BY(不推荐,因为可能影响其他查询)。 3. 使用子查询或派生表,将排序操作放在外层查询。 针对用户提供的SQL,我们可以这样修改(采用第一种方法): 原SQL: ``` with a1 as( select * from music_likes m left join music l on m.music_id = l.id order by music_id ), a2 as( select f.user_id,music_name,music_id from follow f left join a1 on f.follower_id=a1.user_id order by music_id ) select distinct music_name from a2 where user_id = 1 and not exists ( select 1 from a1 where a1.music_id = a2.music_id and a1.user_id = 1 ) order by music_id ``` 错误原因:最外层的SELECT使用了DISTINCT,并且ORDER BY引用了music_id,但music_id不在SELECT列表中(只选了music_name)。 修改方案:在SELECT列表中加入music_id,这样DISTINCT会同时根据music_name和music_id去重,但注意这样可能改变结果集(因为同一个music_name可能有多个music_id?需要根据业务逻辑判断)。如果业务允许,我们可以这样改: 修改后的SQL: ``` ... select distinct music_name, music_id -- 添加music_id from a2 where user_id = 1 and not exists ... order by music_id -- 现在music_id在SELECT列表中了 ``` 但是,如果同一个音乐名称(music_name)对应多个music_id,那么这样查询会返回多条记录(每个music_id一条)。如果这不是我们想要的(即我们只需要音乐名称,不管music_id),那么我们可以考虑第二种方法:使用子查询。 方法二:使用子查询,将去重和排序分离 ``` select music_name from ( select distinct music_name, music_id -- 内层同时选择music_id用于排序 from a2 where user_id = 1 and not exists ... ) as tmp order by music_id -- 外层排序 ``` 注意:内层查询中我们同时选择了music_name和music_id(为了排序),然后外层只选择music_name并按照music_id排序。这样,内层去重后的结果(包括music_id)被用作临时表,外层排序时可以使用music_id,因为它在临时表中存在。 但是,这里有一个问题:同一个music_name可能对应多个music_id,那么内层使用distinct music_name, music_id会返回多条记录,而外层排序时,同一个music_name可能因为不同的music_id而现多次。所以,我们需要根据业务逻辑决定是否可行。 如果业务上要求每个music_name只现一次,那么我们可以考虑使用聚合函数,比如取最小的music_id或最大的music_id来代表这个音乐名称: ``` select music_name from ( select music_name, min(music_id) as min_music_id -- 或者max(music_id) from a2 where user_id = 1 and not exists ... group by music_name -- 按音乐名称分组,每组取一个music_id ) as tmp order by min_music_id -- 然后按照这个取的music_id排序 ``` 这样,每个音乐名称只现一次,并且按照其最小的music_id排序。 因此,具体修改方法需要根据业务需求来定。 如果不希望修改sql_mode(因为修改后可能影响其他查询),那么上述两种方法(添加列或使用子查询)都是可行的。 总结步骤: 1. 分析错误:确认ORDER BY引用的列是否在SELECT列表中。 2. 根据业务需求,选择以下方法之一: a) 将ORDER BY中的列添加到SELECT列表(可能改变结果集的行数,需谨慎)。 b) 使用子查询,在子查询中包含排序所需的列,然后在外层查询中排序(注意结果去重后的影响)。 c) 使用GROUP BY代替DISTINCT,并通过聚合函数处理排序列(如MIN、MAX等)。 针对用户提供的SQL,如果业务上允许同一个music_name对应多个music_id,那么直接添加music_id到SELECT列表即可。如果要求每个music_name只现一次,那么就需要使用聚合函数。 另外,根据引用[3][4]的方法,我们可以通过修改sql_mode来避免这个错误,但不推荐在生产环境中这样做,因为可能会影响其他查询。 修改sql_mode的方法(仅当确实需要时): 在MySQL配置文件(my.ini或my.cnf)的[mysqld]部分添加: sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION 然后重启MySQL服务。 但强烈建议优先修改查询语句。 因此,针对用户提供的SQL,我给两种修改方案(假设业务要求每个music_name只现一次): 方案一(使用聚合函数): ``` with a1 as( select * from music_likes m left join music l on m.music_id = l.id ), a2 as( select f.user_id, music_name, music_id from follow f left join a1 on f.follower_id = a1.user_id ) select music_name from a2 where user_id = 1 and not exists ( select 1 from a1 where a1.music_id = a2.music_id and a1.user_id = 1 ) group by music_name -- 按音乐名称分组 order by min(music_id) -- 按最小的music_id排序 ``` 方案二(使用子查询,并保留music_id用于排序): ``` select music_name from ( select distinct music_name, music_id from a2 where user_id = 1 and not exists ( select 1 from a1 where a1.music_id = a2.music_id and a1.user_id = 1 ) ) as tmp order by music_id ``` 注意:方案二中,如果同一个music_name有多个music_id,那么会现多次。所以需要根据业务选择。 由于原SQL中使用了DISTINCT music_name,所以原意应该是每个music_name只现一次。因此,方案一更符合原意,同时通过聚合函数(min)来获得一个music_id用于排序。 因此,我推荐方案一。 修改后的完整SQL(使用方案一): ``` with a1 as( select * from music_likes m left join music l on m.music_id = l.id ), a2 as( select f.user_id, music_name, music_id from follow f left join a1 on f.follower_id = a1.user_id ) select music_name from a2 where user_id = 1 and not exists ( select 1 from a1 where a1.music_id = a2.music_id and a1.user_id = 1 ) group by music_name order by min(music_id) -- 或者使用max(music_id),根据需求 ``` 这样,我们既保证了每个music_name只现一次,又能够按照music_id排序(通过聚合函数取一个代表值),同时避免了MySQL的语法错误。 最后,关于检查当前sql_mode的方法,可以使用: SELECT @@sql_mode; 关于GROUP BY和DISTINCT的性能区别,通常GROUP BY在功能上更强大(可以配合聚合函数),而DISTINCT只是去重。在去重的场景下,两者性能相近,但具体取决于索引和查询优化器。 以上解决方案适用于MySQL 8.0及以上版本。</think>### 解决 MySQL 中 "ORDER BY 字段不在 SELECT 列表" 的错误 #### 错误原因分析 在 MySQL 8.0+ 版本中,默认启用了 `ONLY_FULL_GROUP_BY` 模式,导致以下冲突: 1. 使用 `DISTINCT` 时,`ORDER BY` 引用了现在 `SELECT` 列表中的字段(如 `music_id`) 2. 违反了 SQL 标准:`DISTINCT` 操作后,排序字段必须明确存在于结果集中[^1][^3] #### 解决方案(推荐三种方法) ##### 方法 1:将排序字段添加到 SELECT 列表(最佳实践) ```sql WITH a1 AS ( SELECT * FROM music_likes m LEFT JOIN music l ON m.music_id = l.id ), a2 AS ( SELECT f.user_id, music_name, music_id -- 确保music_id被选择 FROM follow f LEFT JOIN a1 ON f.follower_id = a1.user_id ) SELECT DISTINCT music_name, music_id -- 添加music_id到SELECT FROM a2 WHERE user_id = 1 AND NOT EXISTS ( SELECT 1 FROM a1 WHERE a1.music_id = a2.music_id AND a1.user_id = 1 ) ORDER BY music_id; -- 现在字段已存在于SELECT列表 ``` **优点**:完全兼容所有 MySQL 版本,符合 SQL 标准[^1][^3] ##### 方法 2:使用子查询分离操作 ```sql SELECT music_name FROM ( SELECT DISTINCT music_name, music_id -- 内层包含排序字段 FROM a2 WHERE user_id = 1 AND NOT EXISTS (...) ) AS tmp ORDER BY music_id; -- 外层使用排序字段 ``` **适用场景**:当不希望排序字段现在最终结果中时 ##### 方法 3:使用聚合函数(替代 DISTINCT) ```sql SELECT music_name FROM a2 WHERE user_id = 1 AND NOT EXISTS (...) GROUP BY music_name -- 替代DISTINCT ORDER BY MIN(music_id); -- 使用聚合函数处理排序字段 ``` **原理**:通过 `GROUP BY` 分组后,使用聚合函数(`MIN/MAX`)引用排序字段[^1] #### 不推荐方案:修改 sql_mode ```sql -- 临时修改(重启失效) SET SESSION sql_mode = 'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION'; -- 永久修改需编辑 my.cnf 文件 [mysqld] sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE... ``` **风险**:禁用 `ONLY_FULL_GROUP_BY` 可能导致其他查询现意外行为[^3][^4] #### 最佳实践建议 1. **优先使用方法 1**,保持查询逻辑清晰 2. 生产环境避免修改 `sql_mode`,确保版本兼容性 3. 使用 `EXPLAIN` 分析查询性能 4. 重要操作前备份数据:`mysqldump -u root -p database > backup.sql` > 通过将 `music_id` 显式添加到 `SELECT` 列表,即可解决该错误。此修改符合 SQL 标准且兼容所有 MySQL 版本[^1][^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值