完了演示的时候报错了!distinct 别乱用啊

1,433 阅读3分钟

前言

昨天敲着敲着代码,小杨哥跑过来给我说,快看他们大会议室演示报错了,还是一堆错了。完了啊在演示的时候报错!!!接下来我们分析一下是什么原因吧。

问题分析

查看日志: 从日志打印看明显的是sql报错了

image.png

这个sql明显是分页查询插件在统计分页条数,日志第一行最后一个单词是distinct关键字,distinct我是从来没有用到过分页中过,这个distinct会根据后面的所有字段去重,性能太差导致连接超时。

小杨哥在这时说,人大金仓distinct只会对第一个字段去重处理,其他字段随机取一条。我当时就否定了他这个观点,有点经验的人也不会这么设计distinct吧,你这不是违反sql规范吗。实际上小杨哥说的那个语法应该是这个DISTINCT ON (column1, column2, ...) column1, column2, ...

我也马上把我结论告知了相关同事,经过测试确实也是因为这个distinct导致请求时长超时。

解决方案

问题已经定位到了,就是distinct后面的字段太多了。用distinct主要目的就是解决 在1对多的连表查询时候,根据主表数据去重。

  • 方式1:使用 distinct on ('id') 指定重复列
  • 方式2:使用group by取代distinct

总结

  1. 在 SQL 中,DISTINCTGROUP BY 都用于处理重复数据,但它们的执行原理和性能特征有所不同。在大多数情况下,GROUP BY 在处理聚合时可能比 DISTINCT 更高效,但实际性能依赖于具体查询和数据集的特性。

    在查询1对多的数据情况,只需要返回主表的数据,这时候distinctdistinct ongroup by 都满足查询结果
    eg: select distinct on (a.id) a.* from atable a left join btable b on a.id= b.a_id where b.name = 'xx'
    这种情况我们可以使用 distinct on (id) ,其他情况 或者是需要调用聚合函数时候就用 group by

  2. 在分页列表查询优化中,我还可以考虑去单独优化count sql
    开发中大多时候我们写分页查询,都是不需要管统计条数这个sql的,因为插件自动帮我们拼装执行了。有些时候需要优化的话,我们可以考虑覆盖统计sql,不动service代码。一般的分页插件在mapper中定义一个命名规则为 queryname +_COUNTmapper就行了,分页插件在执行queryname 查询的的时候,就会优化查询是否有queryname_COOUNT的统计条数SQL,有就使用自定义的。

    自定义的统计sql可以减少查询字段、在不影响统计结果的情况减少连表查询,来提升统计的效率

ps:有服务器需求的联系我返dian,提供技术支持哦

扩展知识 (distinct 和 group by 区别)

  • DISTINCTDISTINCT 用于从查询结果中去除重复的行,返回唯一的值。distinct on ('colunm ..') 指定需要去重的列,可以返回不参与去重的列,没有参与去重的列随机返回一条数据。

  • GROUP BYGROUP BY 用于将结果集按一个或多个列进行分组,并通常与聚合函数(如 COUNTSUM 等)一起使用。不能查询没有参与分组的字段。

性能测试

查询相同的sql 使用日志中的sql作为测试,使用distinctdistinct ongroup by 测试一下执行时间 。用时最短的是 distinct on ,因为只根据一个id进行去重;第二是group by ,毕竟分组的字段太多了差不多30个;distinct (去重30个字段)时间就直接指数级的增长了,列表查询的时候禁用啊。
distinct:14.2s (distinct后只跟一个字段的话就是 0.2s)

1727338873943.jpg distinct on (id): 0.7s

53f7e3e87a4b656d40e8dacc399d57f.png group by: 1.9s (如果只查一个id的话就0.2s)

ae54b66fbed74e54a0684f4ce4c60aa.png