mysql 语句中 group by 的问题

2023-04-28 11:51:31 +08:00
 sniperking1234

有个需求以用户为维度进行统计订单,并显示出用户相关信息,那么我写 sql 的时候,用下面哪种方式比较合适

第一种是把用户信息相关的字段放到 group by 中,在 select 中就也可以显示相应的字段,这样一个 sql 语句就可以查询出来统计信息和用户信息。

select count(userid), user_id ,user_name ,user_type ,user_level ...  from user group by user_id, user_name, user_type, user_level

第二种是先按照 user_id 进行聚合统计,然后再根据 userid 查表,补全用户信息,这样就需要查两次

select count(userid), user_id, ...  from ... group by user_id

select ... from ... where user_id in (...)

第一种方式明显是更简单的,但是把很多冗余的字段放到 group by 的条件中,会不会有性能问题,或者第一种写法可能会带来其他问题,求助下大家的意见,如果有更好的 sql 写法,也希望大家能够帮忙分享一下经验。

ps:无论第一种还是第二种,都需要联表查询

2670 次点击
所在节点    MySQL
28 条回复
dongtingyue
2023-04-28 11:56:35 +08:00
第一种加索引添加测试数据能跑出来就用第一种,自己程序处理不一定比数据库好。
optional
2023-04-28 12:10:10 +08:00
第二种,第一种没有优化空间,压力全部给到数据库。 第二个,还可以看看有没有基于 id 的用户缓存。
甚至,都不应该去 group by
liuxu
2023-04-28 12:24:03 +08:00
第二种,看你是在扫全表,第一个临时表大概率要缓存到磁盘,第二个 sql 可以控制下 in 的数量,业务上并发查询
lysS
2023-04-28 13:55:21 +08:00
in 表达式里面是有列时,它的代价也等价于联表。

而且你这两个不等价吧? 如果只根据 user_id 分组的话,第一个就不对。
sniperking1234
2023-04-28 14:11:20 +08:00
@lysS 第一个是联表查询,sql 没有写全
lysS
2023-04-28 14:15:39 +08:00
那我建议第二种吧
lysS
2023-04-28 14:17:32 +08:00
不过 in 里面的 id 会越来越多吧?
sniperking1234
2023-04-28 14:19:42 +08:00
@lysS 分页显示,理论上 id 数量可控
yinmin
2023-04-28 14:27:13 +08:00
除非记录数上千万级别,否则建议第一种。程序简洁易维护更重要。
liprais
2023-04-28 14:40:42 +08:00
数据量两千万以下就第一种,
两千万以上第二种也不好使,自己搞个 streaming 的拉倒
opengps
2023-04-28 14:48:46 +08:00
为了将来数据量变大时候轻松,一定要选第二种,云架构下的数据库也是尽可能避免高级用法,数据库层面回归原始的语句
BugCry
2023-04-28 14:51:38 +08:00
```
with dt as ( select userid, count(userid) cnt from user group by userid)
select cnt, user.user_id ,user_name ,user_type ,user_level ...
from user, dt
where user.userid = dt.userid
```
我是这样操作的,不知道算不算最佳实践
抛砖引玉哈
sniperking1234
2023-04-28 15:02:32 +08:00
@BugCry 这样写 sql 的确很清晰,不清楚性能如何
xinxingi
2023-04-28 15:29:14 +08:00
对于这个需求,第一种方式是更合适的。使用 group by 语句来聚合数据并显示用户信息是一种常见的 SQL 操作,也是最常用的方式之一。第一种方式可以一次性查询出统计信息和用户信息,而且语句简单易懂。

对于性能问题,将用户信息相关的字段放到 group by 中的确会增加查询语句的复杂度,但是如果你的数据库表结构设计得当,并且使用了正确的索引,那么性能问题应该不大。你可以考虑在 user_id 上创建索引以提高查询性能。

第二种方式需要进行两次查询,这样的话会增加查询的复杂度和执行时间,而且也不太容易理解和维护。因此,如果数据量较大或者查询频率较高,第二种方式可能会导致性能问题。

总的来说,第一种方式更简单、可读性更高,而且性能也不错,因此我建议你采用第一种方式来实现这个需求。
xinxingi
2023-04-28 15:29:31 +08:00
请列出两种方式的空间复杂度和时间复杂度

第一种方式的时间复杂度为 O(NlogN),其中 N 为用户数量,因为查询语句需要进行 group by 操作,这需要对用户表中的数据进行排序。如果你在 user_id 上创建了索引,这个时间复杂度可能会更低。

第一种方式的空间复杂度取决于查询语句返回的行数和每行数据的大小。如果查询结果集中的行数较少,那么空间复杂度也相对较低。如果每行数据的大小较大,查询结果集中的数据量也会增加,从而增加空间复杂度。

第二种方式的时间复杂度为 O(NlogN),其中 N 为用户数量。第一次查询需要对用户表中的数据进行 group by 操作,这需要排序,因此时间复杂度与第一种方式相同。第二次查询需要根据 user_id 来查询用户信息,这个查询的时间复杂度为 O(MlogM),其中 M 为查询结果集中的行数。

第二种方式的空间复杂度取决于第二次查询返回的行数和每行数据的大小。如果第二次查询返回的行数较少,那么空间复杂度也相对较低。如果每行数据的大小较大,查询结果集中的数据量也会增加,从而增加空间复杂度。
xinxingi
2023-04-28 15:36:39 +08:00
上面两段是,gpt 的问答,我个人建议你直接用第一种方式。可读性比第二种高,性能也比第二种更好。如果把数据量计算在内的话,当数据量超出一定量时,直接移交数据部门(大数据)去做,这种分析对于 mpp 架构的数据库来说,小菜一碟 ,OLAP 数据库 干的活就别为难 OLTP 数据库了。
sniperking1234
2023-04-28 15:46:38 +08:00
@xinxingi 哈哈,一眼没看出来是 gpt 回答的
house191
2023-04-28 15:56:56 +08:00
如果只用一张包含用户数据的订单表,第二种写法会输出重复的用户数据。一个用户有五个订单就会在结果里出现五次。还要再套一个去重逻辑。
fiypig
2023-04-28 16:04:48 +08:00
我的话就第一种了, 第二种存在性能问题
sniperking1234
2023-04-28 16:08:08 +08:00
@house191 第二种写法的话,第一条 sql 已经根据 user_id 聚合了,不会出现 user_id 数据重复的问题

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/936176

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX