首先,先简单解释一下笛卡尔积。

现在,我们有两个集合A和B。

A = {0,1}     B = {2,3,4}

集合 A×B 和 B×A的结果集就可以分别表示为以下这种形式:

A×B = {(0,2),(1,2),(0,3),(1,3),(0,4),(1,4)};

B×A = {(2,0),(2,1),(3,0),(3,1),(4,0),(4,1)};

以上A×B和B×A的结果就可以叫做两个集合相乘的‘笛卡尔积’。

从以上的数据分析我们可以得出以下两点结论:

1,两个集合相乘,不满足交换率,既 A×B ≠ B×A;

2,A集合和B集合相乘,包含了集合A中元素和集合B中元素相结合的所有的可能性。既两个集合相乘得到的新集合的元素个数是 A集合的元素个数 × B集合的元素个数;

数据库表连接数据行匹配时所遵循的算法就是以上提到的笛卡尔积,表与表之间的连接可以看成是在做乘法运算。

比如现在数据库中有两张表,student表和 student_subject表,如下所示:

  

我们执行以下的sql语句,只是纯粹的进行表连接。

SELECT * from student JOIN student_subject;
SELECT * from student_subject JOIN student;

看一下执行结果:

  

  表1.0                            表1.1

从执行结果上来看,结果符合我们以上提出的两点结论(红线标注部分);

以第一条sql语句为例我们来看一下他的执行流程,

1,from语句把student表 和 student_subject表从数据库文件加载到内存中。

2,join语句相当于对两张表做了乘法运算,把student表中的每一行记录按照顺序和student_subject表中记录依次匹配。

3,匹配完成后,我们得到了一张有 (student中记录数 × student_subject表中记录数)条的临时表。 在内存中形成的临时表如表1.0所示。我们又把内存中表1.0所示的表称为‘笛卡尔积表’。

  针对以上的理论,我们提出一个问题,难道表连接的时候都要先形成一张笛卡尔积表吗,如果两张表的数据量都比较大的话,那样就会占用很大的内存空间这显然是不合理的。所以,我们在进行表连接查询的时候一般都会使用JOIN xxx ON xxx的语法,ON语句的执行是在JOIN语句之前的,也就是说两张表数据行之间进行匹配的时候,会先判断数据行是否符合ON语句后面的条件,再决定是否JOIN。

  因此,有一个显而易见的SQL优化的方案是,当两张表的数据量比较大,又需要连接查询时,应该使用 FROM table1 JOIN table2 ON xxx的语法,避免使用 FROM table1,table2 WHERE xxx 的语法,因为后者会在内存中先生成一张数据量比较大的笛卡尔积表,增加了内存的开销。

根据上一篇博客(http://www.cnblogs.com/cdf-opensource-007/p/6502556.html),及本篇博客的分析,我们可以总结出一条查询sql语句的执行流程。

From

ON

JOIN

WHERE

GROUP BY

SELECT

HAVING

ORDER BY

LIMIT

最后,针对两张数据库表连接的底层实现,我用java代码模拟了一下,感兴趣的可以看一下,能够帮助我们理解:

package com.opensource.util;

import java.util.Arrays;

public class DecareProduct {

    public static void main(String[] args) {

        //使用二维数组,模拟student表
String[][] student ={
{"0","jsonp"},
{"1","alice"}
}; //使用二维数组,模拟student_subject表
String[][] student_subject2 ={
{"0","0","语文"},
{"1","0","数学"}
}; //模拟 SELECT * from student JOIN student_subject;
String[][] resultTowArray1 = getTwoDimensionArray(student,student_subject2);
//模拟 SELECT * from student_subject JOIN student;
String[][] resultTowArray2 = getTwoDimensionArray(student_subject2,student); int length1 = resultTowArray1.length;
for (int i = 0; i <length1 ; i++) {
System.out.println(Arrays.toString(resultTowArray1[i]));
}
System.err.println("-----------------------------------------------");
int length2 = resultTowArray2.length;
for (int i = 0; i <length2 ; i++) {
System.out.println(Arrays.toString(resultTowArray2[i]));
} } /**
* 模拟两张表连接的操作
* @param towArray1
* @param towArray2
* @return
*/
public static String[][] getTwoDimensionArray(String[][] towArray1,String[][] towArray2){ //获取二维数组的高(既该二维数组中有几个一维数组,用来指代数据库表中的记录数)
int high1 = towArray1.length;
int high2 = towArray2.length; //获取二维数组的宽度(既二位数组中,一维数组的长度,用来指代数据库表中的列)
int wide1 = towArray1[0].length;
int wide2 = towArray2[0].length; //计算出两个二维数组进行笛卡尔乘积运算后获得的结果集数组的高度和宽度,既笛卡尔积表的行数和列数
int resultHigh = high1 * high2;
int resultWide = wide1 + wide2; //初始化结果集数组,既笛卡尔积表
String[][] resultArray = new String[resultHigh][resultWide]; //迭代变量
int index = 0; //先对第二二维数组遍历
for (int i = 0; i < high2; i++) { //拿出towArray2这个二维数组的元素
String[] tempArray = towArray2[i]; //循环嵌套,对第towArray1这个二维数组遍历
for (int j = 0; j < high1; j++) { //初始化一个长度为'resultWide'的数组,作为结果集数组的元素,既笛卡尔积表中的一行
String[] tempExtened = new String[resultWide]; //拿出towArray1这个二维数组的元素
String[] tempArray1 = towArray1[j]; //把tempArray1和tempArray两个数组的元素拷贝到结果集数组的元素中去。(这里用到了数组扩容)
System.arraycopy(tempArray1, 0, tempExtened, 0, tempArray1.length);
System.arraycopy(tempArray, 0, tempExtened, tempArray1.length, tempArray.length); //把tempExtened放入结果集数组中
resultArray[index] = tempExtened; //迭代加一
index++;
}
} return resultArray; } }

执行结果:

  最后说一点,我们作为程序员,研究问题还是要仔细深入一点的。当你对原理了解的有够透彻,开发起来也就得心应手了,很多开发中的问题和疑惑也就迎刃而解了,而且在面对其他问题的时候也可做到触类旁通。当然在开发中没有太多的时间让你去研究原理,开发中要以实现功能为前提,可等项目上线的后,你有大把的时间或者空余的时间,你大可去刨根问底,深入的去研究一项技术,我觉得这对一名程序员的成长是很重要的事情。

mysql(2)—— 由笛卡尔积现象分析数据库表的连接的更多相关文章

  1. MySQL,SQLSERVER,ORACLE获取数据库表名及字段名

    1.MySQL 获取表名: 用“show tables”命令.在程序中也可以采用该命令获取,在返回的RowSet中的“Tables_in_db”读出来.其中“db”是指你的数据库的名称,比如说Tabl ...

  2. MySQL必知必会-官方数据库表及SQL脚本导入生成

    最近在复习SQL语句,看的是MySQL必知必会这本书,但是发现附录中只有表设计,没有表的具体数据.所以在学习相应的语句中体验不是很好,去网上查了数据库的内容,自己慢慢导入到了数据库中.把表放出来作为参 ...

  3. Mysql使用information.shema.tables查询数据库表大小

    简介: information_schema数据库中的表都是只读的,不能进行更新.删除和插入等操作,也不能加触发器,因为它们实际只是一个视图,不是基本表,没有关联的文件. 元数据描述数据的数据,用于描 ...

  4. mysql远程访问另一台主机数据库表,实现小表广播功能

    1.打开navicat,打开任意一个连接,新建一个查询,输入命令 show engines,出现如下界面 2. 如果FEDERATED对应的Support值为NO,则找到C:\ProgramData\ ...

  5. mysql快速生成truncate脚本清空数据库表记录

    语句格式: select CONCAT('truncate TABLE ',table_schema,'.',TABLE_NAME, ';') from INFORMATION_SCHEMA.TABL ...

  6. T-SQL实用查询之分析数据库表的大小

    IF OBJECT_ID('tempdb..#TB_TEMP_SPACE') IS NOT NULL DROP TABLE #TB_TEMP_SPACE GO CREATE TABLE #TB_TEM ...

  7. 数据库表的连接(Left join , Right Join, Inner Join)用法详解

    转自:http://blog.csdn.net/jetjetlinuxsystem/article/details/6663218 Left Join, Inner Join 的相关内容,非常实用,对 ...

  8. MySQL数据库表的设计和优化(上)

    一.单表设计与优化: (1)设计规范化表,消除数据冗余(以使用正确字段类型最明显):数据库范式是确保数据库结构合理,满足各种查询需要.避免数据库操作异常的数据库设计方式.满足范式要求的表,称为规范化表 ...

  9. 【SQL server初级】数据库性能优化二:数据库表优化

    数据库优化包含以下三部分,数据库自身的优化,数据库表优化,程序操作优化.此文为第二部分 数据库性能优化二:数据库表优化 优化①:设计规范化表,消除数据冗余 数据库范式是确保数据库结构合理,满足各种查询 ...

随机推荐

  1. Spring Boot 系列(八)@ControllerAdvice 拦截异常并统一处理

    在spring 3.2中,新增了@ControllerAdvice 注解,可以用于定义@ExceptionHandler.@InitBinder.@ModelAttribute,并应用到所有@Requ ...

  2. TypeScript入门(一)

    TypeScript是微软官方的一种语言,是JavaScript的超集.它遵循的ECMA Script 6.0是下一代的JavaScript.浏览器还没有完全支持ES6,而ES5是弱类型的语言,还没有 ...

  3. nodejs批量导入数据eventproxy(回调函数嵌套解决方案)使用实例

    回调函数嵌套解决方案——eventProxy API地址:https://github.com/JacksonTian/eventproxy 1.安装eventproxy 执行npm install ...

  4. Vue:渲染、指令、事件、组件、Props、Slots

    如果要我用一句话描述使用 Vue 的经历,我可能会说“它如此合乎常理”或者“它提供给我需要的工具,而且没有妨碍我的工作”.每当学习 Vue 的时候,我都很高兴,因为很有意义,而且很优雅. 以上是我对 ...

  5. MYSQL数据库学习五 表的操作和约束

    5.1 表的基本概念 表示包含数据库中所有数据的数据库对象.一行代表唯一的记录,一列代表记录的一个字段. 列(Columns):属性列,创建表时必须指定列名和数据类型. 索引(Indexes):根据指 ...

  6. EF Core利用Transaction对数据进行回滚保护

    What? 首先,说一下什么是EF Core中的Transaction Transaction允许以原子方式处理多个数据库操作,如果事务已提交,则所有操作都应用于数据库,如果事务回滚,则没有任何操作应 ...

  7. Matlab绘图基础——绘制等高线图

    % 等高线矩阵的获取 C = contourc(peaks(20),3);              % 获取3个等级的等高线矩阵 % 等高线图形的绘制 contour(peaks(20),10);c ...

  8. Oracle 琐表和查询谁在琐表并解决

    Oracle数据库操作中,我们有时会用到锁表查询以及解锁和kill进程等操作,那么这些操作是怎么实现的呢?本文我们主要就介绍一下这部分内容. (1)锁表查询的代码有以下的形式: select coun ...

  9. 关于VR开发中的穿墙问题随想

    在VR开发中,用户将以第一人称的视角进入虚拟世界,即用户同时身处两个坐标系:1. 现实世界坐标系(如房间的坐标系),用户的身体处于这个坐标系 2. VR世界坐标系,用户的感官处于这个坐标系,即用户觉得 ...

  10. ThreadLocal 原理和使用场景分析

    ThreadLocal 不知道大家有没有用过,但至少听说过,今天主要记录一下 ThreadLocal 的原理和使用场景. 使用场景 直接定位到 ThreadLocal 的源码,可以看到源码注释中有很清 ...