在一个很偶然的场景下,我看到了一个关于数据库中间件的SQL测试,对比测试的内容大体是对于一条查询语句的输出。看到输出结果,虽然结果是客观的,但是我总是感觉缺少了些什么,于是做了下测试。

为了简化起见,我们把测试场景做到至简。创建两张表,就1个字段,4行记录,来说明下JOIN的一些问题和隐患。

但凡不是太懒的话,这个场景都可以很快实现的。

mysql> create table test1(id int);

mysql> create table test2(id int)

mysql> insert into test2 values(1),(2),(2),(3)

mysql> insert into test1 values (1),(2),(2),(3);

和我们预期的一样,这2张表的输出就是4行记录。

mysql> select *from test1;

+------+

| id |

+------+

| 1 |

| 2 |

| 2 |

| 3 |

+------+

4 rows in set (0.00 sec)

数据情况是完全一样的。

mysql> select *from test2;

+------+

| id |

+------+

| 1 |

| 2 |

| 2 |

| 3 |

+------+

4 rows in set (0.00 sec)

对于下面的SQL,你猜猜分别会有几行输出结果。

select * from test1 m,test2 n where m.id=n.id and n.id=2;

select m.id from test1 m,test2 n where m.id=n.id and n.id=2;

可以先思考几秒钟,再往下看。

2

输出结果如下:

mysql> select * from test1 m,test2 n where m.id=n.id and n.id=2;

+------+------+

| id | id |

+------+------+

| 2 | 2 |

| 2 | 2 |

| 2 | 2 |

| 2 | 2 |

+------+------+

4 rows in set (0.00 sec)

第2条SQL的输出如下:

mysql> select m.id from test1 m,test2 n where m.id=n.id and n.id=2;

+------+

| id |

+------+

| 2 |

| 2 |

| 2 |

| 2 |

+------+

4 rows in set (0.00 sec)

结果是不是很简单,当然我要表达的不是这一层含义,我想要说的是结果和我们的需求其实是存在一些偏差。

3

从我们的预期来看,输出既然是m(test1)的数据,那么m应该是作为驱动表,那么从我们的常规思路来看,应该是期望看到2条记录。

因为m(test1)表一共就4行记录,一共输出了4行,而且有2行还是完全一样的,对于需求来说实在是找不出有什么场景需要这样的预期结果。

如果要实现这种需求,显然使用distinct,group by是不等价的。

mysql> select distinct m.id from test1 m join test2 n on m.id=n.id and m.id=2 ;

+------+

| id |

+------+

| 2 |

+------+

1 row in set (0.00 sec)

所以要实现这种需求,一种很自然的处理方式就浮出水面,那就是半连接。

mysql> select m.id from test1 m where m.id in (select n.id from test2 n where m.id=n.id and n.id=2);

+------+

| id |

+------+

| 2 |

| 2 |

+------+

2 rows in set (0.00 sec)

还有一种是exists,在MySQL中其实是更偏爱exists的方式的。

mysql> select m.id from test1 m where exists (select 1 from test2 n where m.id=n.id and n.id=2);

+------+

| id |

+------+

| 2 |

| 2 |

+------+

2 rows in set (0.00 sec)

可以看到在这种场景下,从SQL要表达的含义层面才是符合我们的需求出发点的。

我们来看看使用单纯的JOIN带来的一些副作用。

4

第一个是过滤数据的偏差,按照distinct,group by的处理方式是始终做唯一性处理的,也就意味着这种场景下只有1行记录输出。

mysql> select distinct m.id from test1 m join test2 n on m.id=n.id where n.id=2;

+------+

| id |

+------+

| 2 |

+------+

1 row in set (0.00 sec)

第二个是带来的数据统计偏差

我们其实想看一下匹配的记录,预期是2行,但是输出了4行,如果数据量较大的情况下,这种查询导致的结果影响面就足够大。

mysql> select count(*) from test1 m join test2 n on m.id=n.id where n.id=2;

+----------+

| count(*) |

+----------+

| 4 |

+----------+

1 row in set (0.00 sec)

所以很多不好的查询习惯就开始了,比如:

mysql> select count(*) from (select m.id from test1 m join test2 n on m.id=n.id where n.id=2) t;

+----------+

| count(*) |

+----------+

| 4 |

+----------+

1 row in set (0.00 sec)

而这种逻辑方式就很容易适配了。

mysql> select count(*) from test1 m where exists (select 1 from test2 n where m.id=n.id and n.id=2);

+----------+

| count(*) |

+----------+

| 2 |

+----------+

1 row in set (0.00 sec)

第三点影响最大,也是我们最容易忽略的。那就是去重过滤带来的副作用。

我们知道输出结果是4行,但是我们预期的是2行,所以如果处理得当,我们需要过滤的数据比例就是50%,而如果匹配记录数是3,则过滤的数据比例是1-3/3^2将近70%,所以一个很基本的公式 1- N/N^2=1-1/N,过滤比例是很高的,如果匹配的记录数是100,那么常规的SQL处理要过滤的就是99.99%的数据。这个过滤比例实在是太高了。

或者换一个问法,如何在1万条记录中如何有效的过滤掉99%以上的数据,可想而知这个复杂度和资源消耗。

第四点,如果是在分布式场景中,那么这个影响的面会被最大化,复杂度和消耗可能是和节点数成正比的。

5

到了这里,会发现我需求出发点的JOIN竟然会变得如此复杂。而换个角度来看,其实就容易理解在我们优化中经常看到的一些distinct和一些看起来蹩脚的组合查询了。

换句话来说,我们的需求是什么,从需求相关的驱动表来入手,可能也是我们需要明白的一个优化点。

@kafkalistener中id的作用_无意中测试了下MySQL里面的join操作,发现还是存在理解偏差...相关推荐

  1. 小说中人称转换作用_小说中人称转换作用

    人称作用专题阅读练习 人称专题阅读练习 [阅读目标] 通过阅读指导和训练,弄清文章记叙人称的概念,掌握人称的分类和表达作用,更好地理清文章思路,理 解文章内容,把握文章的中心思想和作者的感情立场. [ ...

  2. mysql中id是什么_数据库中的id是什么

    {"moduleinfo":{"card_count":[{"count_phone":1,"count":1}],&q ...

  3. B表中的pid对应A表中id,查询A表中数据,根据b表中对应a表中该id的数据数目排序...

    B表中的pid对应A表中id,查询A表中数据,根据b表中对应a表中该id的数据数目排序 select a.*,count(*) as c from a left join b on a.id=b.ai ...

  4. mvp中的m作用_将M放入MVP

    mvp中的m作用 by Howard Lo 霍华德·罗 将M放入MVP (Putting the M in MVP) Years ago, I thought I knew what my minim ...

  5. vue列表渲染中key的作用_列表渲染和Vue的v-for指令

    Web渲染是Web开发中最常用的实战之一.动态列表渲染通常用于简洁友好的格式向用户渲染一系列相似的分组信息.在我们使用的每个Web应用程序中,都可以看到很多内容列表被用于Web应用程序当中. 在这篇文 ...

  6. java中execution的作用_一文初步了解Java虚拟机

    大家都知道,Java中JVM的重要性,学习了JVM你对Java的运行机制.编译过程和如何对Java程序进行调优相信都会有一个很好的认知. 什么是JVM? JVM(Java Virtual Machin ...

  7. Apache ZooKeeper - 集群中 Leader 的作用_事务的请求处理与调度分析

    文章目录 事务性请求处理 Leader 事务处理分析 预处理阶段 事务处理阶段 事务执行阶段 响应阶段 源码分析 小结 Leader 服务器在 ZooKeeper 中的作主要是处理事务性的会话请求以及 ...

  8. python中headers的作用_爬虫入门到精通-headers的详细讲解(模拟登录知乎)

    直接开始案例吧. 本次我们实现如何模拟登陆知乎. 1.抓包 注意打开开发者工具后点击"preserve log",密码记得故意输入错误,然后点击登录 我们很简单的就找到了 我们需要 ...

  9. python 中缩进的作用_缩进在Python中的作用有哪些?Python缩进案例分享

    缩进通常是指在文档中缩进单词,空格或行以遵循文档的样式规则,或者可以用来解释编写文档或代码时应使用的距离或应使用的空格.不过缩进在Python中的作用有哪些?在Python中缩进是最独特的功能之一,可 ...

最新文章

  1. 开源软件License汇总
  2. 复习计算机网络day1-计算机网络的初步了解
  3. 拥抱大家庭,nodejs走thrift
  4. 开盘即破发,网易有道赴美上市,继续发力在线教育
  5. JavaWeb(九)——JavaBean、Filter
  6. 微信小程序实现滚动到指定位置
  7. Linux集群和自动化维1.4.2 优化Linux下的内核TCP参数以提高系统性能
  8. python重点知识 钻石_python——子类对象如何访问父类的同名方法
  9. java中ssm付款代码,ssm实现支付宝支付功能(图文详解)
  10. 腾讯面试Android必问11题,我说的,不信就来看看
  11. 【Python3网络爬虫开发实战】1.2.4-GeckoDriver的安装
  12. (C# TCP异步)客户端异常断开,服务器利用KeepAlive监测
  13. 华为服务器修改登录密码,服务器登录修改密码
  14. Windows系统镜像下载
  15. win10 账户密码忘记的解决办法
  16. 重置Windows 7网络设置
  17. Java创建单例模式(singleton)的几种实现方式
  18. 电磁场与电磁波实验 01 - | 位移电流测量及电磁场与电磁波的存在实验
  19. java编程10以内随机数字乘除,Java实现随机出题,10道10以内加减法计算代码实例...
  20. vs2008编译QT开源项目--太阳神三国杀源码分析(五) 分牌及出牌

热门文章

  1. react-native中的动画
  2. mpvue使用vant Weapp运行npm run build命令打包后失效
  3. HTMLCSS常见问题整理(四)
  4. 解决outlook无法启动
  5. 什么是每点价值量(Dollar Per Point)
  6. Linux Bash Shell学习(十八):String I/O——echo和printf
  7. Android WebView 调起H5支付,提示商家参数格式有误
  8. 从0开始架构一个IOS程序—— 02 — 设置启动图标与启动页面
  9. Leetcode-1154 Ordinal Number Of Date(一年中的第几天)
  10. x == (x = y) 不等于 (x = y) == x ?