mysql(2)—— 由笛卡尔积现象分析数据库表的连接

摘要:
首先,让我们简要解释一下笛卡尔积。将两个集合相乘得到的新集的元素数为A集合的元素数×B集合的元素数目;连接数据库表的数据行时要遵循的算法是上面提到的笛卡尔积。表格之间的联系可以看作是乘法。例如,数据库中有两个表:student表和student_主题表如下所示:我们执行以下sql语句,仅用于表连接。我们还将内存中表1.0所示的表称为“笛卡尔积表”。

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

现在,我们有两个集合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表,如下所示:

mysql(2)—— 由笛卡尔积现象分析数据库表的连接第1张  mysql(2)—— 由笛卡尔积现象分析数据库表的连接第2张

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

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

看一下执行结果:

mysql(2)—— 由笛卡尔积现象分析数据库表的连接第3张  mysql(2)—— 由笛卡尔积现象分析数据库表的连接第4张

  表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)—— 由笛卡尔积现象分析数据库表的连接第5张

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

mysql(2)—— 由笛卡尔积现象分析数据库表的连接第6张

免责声明:文章转载自《mysql(2)—— 由笛卡尔积现象分析数据库表的连接》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇[模拟赛]五子棋 题解Java基础-对象的内存分配与初始化(一定要明白的干货)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

调用支付宝转账接口(单笔)

下面这几个类都是支付宝demo里面的,直接拿过来用就可以 using System.Web; using System.Text; using System.IO; using System.Net; using System; using System.Collections.Generic; namespace Com.Alipay { pu...

product of大数据平台搭建------CM 和CDH安装

一、安装说明       CM是由cloudera公司提供的大数据组件自动部署和监控管理工具,相应的和CDH是cloudera公司在开源的hadoop社区版的基础上做了商业化的封装的大数据平台。        采用离线安装模式,虽然在线安装比较简单,但是速度感人,原因大家都懂,这里我使用的各软件版本信息为: 操作系统:CentOS6.8 CDH版本:5.7...

Java多次启动相同jar程序

背景现在很多软件都支持集群部署,但是测试环境通常资源有限,所以一般通过单台机器模拟集群部署(使用不同端口,运行相同jar包),本文的目的就是通过多种方式实现此需求。 两个程序1、jar程序   ① springboot程序   ② 只包含一个main方法,用于启动程序,输出进程ID   ③ 路径:C:/demo.jar(windows) /demo.jar...

dedecms 空间迁移步骤

1.在新空间重新安装一次原版本的DEDECMS,然后把旧站的所有数据,这里的数据指的是文件,即除了根目录下文件夹include下的配置文件config_base.php外的所有文件覆盖到新空间下2.在旧网站后台系统管理-数据备份哪里备份数据,这里的数据指的是DEDECMS自己后台所采取的备份文件,类似用phpmyadmin等工具所作的备份。备份完成后,DE...

Java数据库连接--JDBC基础知识(操作数据库:增删改查)

一、JDBC简介   JDBC是连接java应用程序和数据库之间的桥梁。   什么是JDBC?   Java语言访问数据库的一种规范,是一套API。   JDBC (Java Database Connectivity) API,即Java数据库编程接口,是一组标准的Java语言中的接口和类,使用这些接口和类,Java客户端程序可以访问各种不同类型的数据库...

octave基本操作

参考:https://blog.csdn.net/iszhenyu/article/details/78712228; 吴恩达机器学习视频; 在学习机器学习的过程中,免不了要跟MATLAB、Octave打交道,这两个工具都可以帮助我们很好的解决数值计算问题,两者的语法也非常接近。 Octave是一个完全开源免费的软件,无论是Windows还是Mac环境都可...