由笛卡尔积现象分析数据库表的连接

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

现在,我们有两个集合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;

    }

}

执行结果:

时间: 2024-10-22 06:44:20

由笛卡尔积现象分析数据库表的连接的相关文章

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

left join 是left outer join的简写,left join默认是outer属性的. Inner Join Inner Join 逻辑运算符返回满足第一个(顶端)输入与第二个(底端)输入联接的每一行.这个和用select查询多表是一样的效果,所以很少用到: outer join则会返回每个满足第一个(顶端)输入与第二个(底端)输入的联接的行.它还返回任何在第二个输入中没有匹配行的第一个输入中的行.关键就是后面那句,返回的多一些.所以通常意义上的left join就是left o

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

IF OBJECT_ID('tempdb..#TB_TEMP_SPACE') IS NOT NULL DROP TABLE #TB_TEMP_SPACE GO CREATE TABLE #TB_TEMP_SPACE( NAME VARCHAR(500) ,ROWS INT ,RESERVED VARCHAR(50) ,DATA VARCHAR(50) ,INDEX_SIZE VARCHAR(50) ,UNUSED VARCHAR(50) ) GO SP_MSFOREACHTABLE 'INSER

数据库性能优化二:数据库表优化

数据库优化包含以下三部分,数据库自身的优化,数据库表优化,程序操作优化.此文为第二部分 数据库性能优化二:数据库表优化 优化①:设计规范化表,消除数据冗余 数据库范式是确保数据库结构合理,满足各种查询需要.避免数据库操作异常的数据库设计方式.满足范式要求的表,称为规范化表,范式产生于20世纪70年代初,一般表设计满足前三范式就可以,在这里简单介绍一下前三范式 先给大家看一下百度百科给出的定义: 第一范式(1NF)无重复的列 所谓第一范式(1NF)是指在关系模型中,对域添加的一个规范要求,所有的域

数据库(join) 内连接、外连接、笛卡尔积

今天,重刷了刷leetcode数据库的题目,对数据库连接查询做一个记录. 数据库(join) 内连接.外连接.笛卡尔积 内连接(自身连接) 内连接有隐式内连接和显示内连接两种: 隐式(无join),from后跟多个表名,生成的中间临时表是全部笛卡尔积.最后用where条件筛选 例如: select A.Name from Employee as A, Employee as B where A.ManagerId=B.Id and A.Salary>B.Salary 显示(有join),中间临时

用VC++操作ACESS数据库(创建数据库、建立新表、连接、增删查改)

转载http://liheng19870711.blog.163.com/blog/static/8716802201121724528232/进行修改 用VC++操作ACESS数据库(创建数据库.建立新表.连接.增删查改) 首先在StdAfx.h中包含如下头文件#import "C:\Program Files\Common Files\system\ado\msadox.dll"  // 创建数据库必用#import"C:\Program Files\Common Fil

通过 jdbc 分析数据库中的表结构和主键外键

文章转自:http://ivan4126.blog.163.com/blog/static/20949109220137753214811/ 在某项目中用到了 hibernate ,大家都知道 hibernate 是 ORM 框架,他是有能力根据实体生成数据库表的.我们在单元测试的时候用到了 dbUnit ,dbUnit 可以帮助我们在测试前把数据库的测试数据准备好,然后我们就利用现成的数据库环境测试,测试完成后需将数据库中的所有数据清除(为了不影响其他的单元测试),然后接着下一个测试.虽然已经

数据库表与表的连接方式

连接查询方式有: 内连接.外连接(左连接.右连接.全连接).交叉连接 左连接和右连接的区别: 左连接以左表为基准进行查询,左表数据会全部显示出来,右表如果和左表匹配的数据则显示相应字段的数据,如果不匹配,则显示为NULL;右连接刚好相反. 全连接就是先以左表进行左外连接,然后以右表进行右外连接. 说明:所谓的基准,就是以某张表的限制条件查询条件为准! 具体如下: 一.内连接 内连接查询操作列出与连接条件匹配的数据行,它使用比较运算符比较被连接列的列值.内连接分三种: 1.等值连接:在连接条件中使

20170105数据库表设计知识点

20170105数据库表设计知识点 ------指导老师    星哥 1.PHP(MYSQL)擅长单表操作,不要做过多无谓的连接查询 2.表字段名不要使用大驼峰命名方式,最好采用下划线,命名要和团队习惯一致,通俗易懂. 3.表级.字段都要有注释 4.MyISAM 适合于一些需要大量查询的应用,但其对于有大量写操作并不是很好.甚至你只是需要update一个字段,整个表都会被锁起来,而别的进程,就算是读进程都无法操作直到读操作完成.另外,MyISAM 对于 SELECT COUNT(*) 这类的计算

SQL应用与开发:(五)多个数据表的连接

数据库中的各个表中存储着不同的数据,用户往往需要用多个表中的数据来组合.提炼出所需要的信息.如果一个查询需要对多个表进行操作,就称为连接查询.连接查询的结果集或结果表称为表之间的连接.查询实际上是通过各个表之间共同列的关联性来查询数据的,它是关系型数据库查询最主要的特征. 实现连接的结果是在向数据库增添新类型的数据方面没有限制,具有很大的灵活性.通常总是通过连接创建一个新表,以包含不同表中的数据.如果新表有合适的域,就可以将它连接到现有的表. 1.简单连接操作 最简单的连接方式是在SELECT语