本篇文章中,主要说明SQL中的各种连接以及使用范围,以及更进一步的解释关系代数法和关系演算法对在同一条查询的不同思路。

多表连接简介

在关系数据库中,一个查询往往会涉及多个表,因为很少有数据库只有一个表,而如果大多查询只涉及到一个表的,那么那个表也往往低于第三范式,存在大量冗余和异常。

因此,连接(Join)就是一种把多个表连接成一个表的重要手段.

比如简单两个表连接学生表(Student)和班级(Class)表,如图:

进行连接后如图:

笛卡尔积

笛卡尔积在SQL中的实现方式既是交叉连接(Cross Join)。所有连接方式都会先生成临时笛卡尔积表,笛卡尔积是关系代数里的一个概念,表示两个表中的每一行数据任意组合,上图中两个表连接即为笛卡尔积(交叉连接)

在实际应用中,笛卡尔积本身大多没有什么实际用处,只有在两个表连接时加上限制条件,才会有实际意义,下面看内连接

内连接

如果分步骤理解的话,内连接可以看做先对两个表进行了交叉连接后,再通过加上限制条件(SQL中通过关键字on)剔除不符合条件的行的子集,得到的结果就是内连接了.上面的图中,如果我加上限制条件

对于开篇中的两个表,假使查询语句如下:

		SELECT *
FROM [Class] c
inner join
[Student] s
on c.ClassID=s.StudentClassID

可以将上面查询语句进行分部理解,首先先将Class表和Student表进行交叉连接,生成如下表:

然后通过on后面的限制条件,只选择那些StudentClassID和ClassID相等的列(上图中划了绿色的部分),最终,得到选择后的表的子集

当然,内连接on后面的限制条件不仅仅是等号,还可以使用比较运算符,包括了>(大于)、>=(大于或等于)、<=(小于或等于)、<(小于)、!>(不大于)、!<(不小于)和<>(不等于)。当然,限制条件所涉及的两个列的数据类型必须匹配.

对于上面的查询语句,如果将on后面限制条件由等于改为大于:

		SELECT *
FROM [Class] c
inner join
[Student] s
on c.ClassID>s.StudentClassID

则结果从第一步的笛卡尔积中筛选出那些ClassID大于StudentClassID的子集:

虽然上面连接后的表并没有什么实际意义,但这里仅仅作为DEMO使用:-)

关系演算

上面笛卡尔积的概念是关系代数中的概念,而我在前一篇文章中提到还有关系演算的查询方法.上面的关系代数是分布理解的,上面的语句推导过程是这样的:“对表Student和Class进行内连接,匹配所有ClassID和StudentClassID相等行,选择所有的列”

而关系演算法,更多关注的是我想要什么,比如说上面同样查询,用关系演算法思考的方式是“给我找到所有学生的信息,包括他们的班级信息,班级ID,学生ID,学生姓名”

用关系演算法的SQL查询语句如下:

		SELECT *
FROM [Class] c
,
[Student] s
where c.ClassID=s.StudentClassID

当然,查询后返回的结果是不会变的:

外连接

假设还是上面两个表,学生和班级.我在学生中添加一个名为Eric的学生,但出于某种原因忘了填写它的班级ID:

 

当我想执行这样一条查询:给我取得所有学生的姓名和他们所属的班级:

		SELECT s.StudentName,c.ClassName 

  FROM [fordemo].[dbo].[Student] s
inner join
[fordemo].[dbo].[Class] c
on
s.StudentClassID=c.ClassID

结果如下图:

可以看到,这个查询“丢失”了Eric..

这时就需要用到外连接,外连接可以使连接表的一方,或者双方不必遵守on后面的连接限制条件.这里把上面的查询语句中的inner join改为left outer join:

		SELECT s.StudentName,c.ClassName 

  FROM [fordemo].[dbo].[Student] s
left outer join
[fordemo].[dbo].[Class] c
on
s.StudentClassID=c.ClassID

结果如下:

Eric又重新出现.

右外连接

右外连接和左外连接的概念是相同的,只是顺序不同,对于上面查询语句,也可以改成:

		SELECT s.StudentName,c.ClassName 

  FROM [fordemo].[dbo].[Class] c
right outer join
[fordemo].[dbo].[Student] s
on
s.StudentClassID=c.ClassID

效果和上面使用了左外连接的效果是一样的.

全外连接

全外连接是将左边和右边表每行都至少输出一次,用关键字”full outer join”进行连接,可以看作是左外连接和右外连接的结合.

自连接

谈到自连接,让我们首先从一个表和一个问题开始:

上面员工表(Employee),因为经理也是员工的一种,所以将两种人放入一个表中,MangerID字段表示的是当前员工的直系经理的员工id.

现在,我的问题是,如何查找CareySon的经理的姓名?

可以看出,虽然数据存储在单张表中,但除了嵌套查询(这个会在后续文章中讲到),只有自连接可以做到.正确自连接语句如下:

		SELECT m.EmployeeName
FROM [fordemo].[dbo].[Employee] e
inner join [fordemo].[dbo].[Employee] m
on e.ManagerID=m.id and e.EmployeeName='Careyson'

在详细解释自连接的概念之前,请再看一个更能说明自连接应用之处的例子:

这个表是一个出席会议记录的表,每一行表示出席会议的记录(这里,由于表简单,我就不用EmployeeID和MeetingID来表示了,用名称对于理解表更容易些)

好了,现在我的问题是:找出既参加“谈论项目进度”会议,又参加”讨论职业发展”会议的员工

乍一看上去很让人迷惑是吧,也许你看到这一句脑中第一印象会是:

		SELECT  EmployeeName
FROM [fordemo].[dbo].[MeettingRecord] m
where MeetingName='¨???????????¨¨' and meetingName='¨???????¨°???¤?é?1'

(我用的代码高亮插件不支持中文,所以上面where子句后面第一个字符串是’谈论项目进度’,第二个是’讨论职业发展’)

恩,恭喜你,答错了…如果这样写将会什么数据也得不到.正确的写法是使用自连接!

自连接的是一种特殊的连接,是对物理上相同但逻辑上不相同的表进行连接的方式。我看到百度百科上说自连接是一种特殊的内连接,但这是错误的,因为两个相同表之间不光可以内连接,还可以外连接,交叉连接…在进行自连接时,必须为其中至少一个表指定别名以对这两个表进行区分!

回到上面的例子,使用自连接,则正确的写法为:

		SELECT  m.EmployeeName
FROM [fordemo].[dbo].[MeettingRecord] m,
[fordemo].[dbo].[MeettingRecord] m2
where m.MeetingName='¨???????????¨¨' and m2.MeetingName='¨???????¨°???¤?é?1'
and m.EmployeeName=m2.EmployeeName

(关于乱码问题,请参考上面)

多表连接

多个表连接实际上可以看成是对N个表进行n-1次双表连接.这样理解会让问题简单很多!

  

比如上面三个表,前两个表是我们已经在文章开始认识的,假设现在又添加了一个教师表,对这三个表进行笛卡尔积如下:

		SELECT *
FROM [fordemo].[dbo].[Class]
cross join
[fordemo].[dbo].[Teacher]
cross join
[fordemo].[dbo].[Student]

结果可以如图表示:

总结

文中对SQL中各种连接查询方式都做了简单的介绍,并利用一些Demo实际探讨各种连接的用处,掌握好各种连接的原理是写好SQL查询所必不可少的!

-------------------------------------------------------------

没有join条件导致笛卡尔乘积
学过线性代数的人都知道,笛卡尔乘积通俗的说,就是两个集合中的每一个成员,都与对方集合中的任意一个成员有关联。可以想象,在SQL查询中,如果对两张表join查询而没有join条件时,就会产生笛卡尔乘积。这就是我们的笛卡尔乘积导致的性能问题中最常见的案例:开发人员在写代码时遗漏了join条件。

发生笛卡尔乘积的sql:

view plaincopy to clipboardprint?select sum(project_fj.danjia*project_fj.mianji) from project_fj,orderform where project_fj.zhuangtai='未售' and project_fj.project_id=30 
select sum(project_fj.danjia*project_fj.mianji) from project_fj,orderform where project_fj.zhuangtai='未售' and project_fj.project_id=30

这个语句其实只是sql语句的一部分,问题是另一部分用到了表orderform,所以from中有orderform,但是上面的这部分语句完全没有用到orderform,但是不设置条件就导致了笛卡尔乘积。

解决方法:使用LEFT JOIN

view plaincopy to clipboardprint?select sum(project_fj.danjia*project_fj.mianji) from project_fj LEFT JOIN orderform ON project_fj.id=orderform.project_id 
where project_fj.zhuangtai='未售' and project_fj.project_id=30 
select sum(project_fj.danjia*project_fj.mianji) from project_fj LEFT JOIN orderform ON project_fj.id=orderform.project_id
where project_fj.zhuangtai='未售' and project_fj.project_id=30

本文出自“suixufeng的专栏”

[转]sql语句中出现笛卡尔乘积 SQL查询入门篇的更多相关文章

  1. sql语句中出现笛卡尔乘积 SQL查询入门篇

    2014-12-29  凡尘工作室   阅 34985  转 95 本篇文章中,主要说明SQL中的各种连接以及使用范围,以及更进一步的解释关系代数法和关系演算法对在同一条查询的不同思路. 多表连接简介 ...

  2. sql语句中出现笛卡尔乘积

    没有join条件导致笛卡尔乘积 学过线性代数的人都知道,笛卡尔乘积通俗的说,就是两个集合中的每一个成员,都与对方集合中的任意一个成员有关联.可以想象,在SQL查询中,如果对两张表join查询而没有jo ...

  3. SQL语句中带有EXISTS谓词的子查询的理解与使用

    EXISTS:代表存在量词. 在SQL中,把具有全称量词的谓词查询问题转换成等价的存在量词的谓词查询予以实现. 如有三个表,Student(Sno,Sname),Course(Cno,Cname),S ...

  4. (转载)总结一下SQL语句中引号(')、quotedstr()、('')、format()在SQL语句中的用法

    总结一下SQL语句中引号(').quotedstr().('').format()在SQL语句中的用法 总结一下SQL语句中引号(').quotedstr().('').format()在SQL语句中 ...

  5. SQL点滴31—SQL语句中@@IDENTITY和@@ROWCOUNT区别

    原文:SQL点滴31-SQL语句中@@IDENTITY和@@ROWCOUNT区别 SQL语句中@@IDENTITY和@@ROWCOUNT区别 在一条 INSERT.SELECT INTO 或大容量复制 ...

  6. (转载)总结一下SQL语句中引号(')、quotedstr()、('')、format()在SQL语句中的用法

    总结一下SQL语句中引号(').quotedstr().('').format()在SQL语句中的用法以及SQL语句中日期格式的表示(#).('')在Delphi中进行字符变量连接相加时单引号用('' ...

  7. Mysql训练:两个表中使用 Select 语句会导致产生 笛卡尔乘积 ,两个表的前后顺序决定查询之后的表顺序

    力扣:超过经理收入的员工 Employee 表包含所有员工,他们的经理也属于员工.每个员工都有一个 Id,此外还有一列对应员工的经理的 Id. +----+-------+--------+----- ...

  8. ASP.NET MVC中实现属性和属性值的组合,即笛卡尔乘积02, 在界面实现

    在"ASP.NET MVC中实现属性和属性值的组合,即笛卡尔乘积01, 在控制台实现"中,在控制台应用程序中实现了属性值的笛卡尔乘积.本篇在界面中实现.需要实现的大致如下: 在界面 ...

  9. ASP.NET MVC中实现属性和属性值的组合,即笛卡尔乘积01, 在控制台实现

    在电商产品模块中必经的一个环节是:当选择某一个产品类别,动态生成该类别下的所有属性和属性项,这些属性项有些是以DropDownList的形式存在,有些是以CheckBoxList的形式存在.接着,把C ...

随机推荐

  1. oracle-3-子查询和常用函数

    主要内容: >子查询 >伪例 >锁的概念 >>>1.子查询 子查询在SELECT ,UPDATE ,DELETE 语句内部可以出现SELECT 语句,内部的SELE ...

  2. c# 多线程 调用带参数函数

    线程操作主要用到Thread类,他是定义在System.Threading.dll下.使用时需要添加这一个引用.该类提供给我们四个重载的构造函数(以下引自msdn).       Thread (Pa ...

  3. zabbix_监控_端口

        目的:监控web主机80端口是否在供提服务.如果不在发出报警.配置:    1.添加监控项(Items).        打开zabbix web管理界面:选择"Configurat ...

  4. Google-解决在调试页面或者js时总是提示烦恼的断点问题

    按F12键,然后切换到Source标签,看底下的那个跟暂停一样的图标是不是变成蓝色或紫色了? 如果是蓝色或者紫色,则把他切换到“灰色”状态(点击图标就会切换成不同的状态.或者可能是其他颜色状态),如下 ...

  5. scanf的一些技巧

    一.scanf和gets 1.不同点: char string[50]; scanf("%s",string); //当遇到回车,空格和tab键会自动在字符串后面添加'\0',但是 ...

  6. SQL多条件查询

    SELECT a.tel,a.business_code,b.name AS business_name,a.register_time FROM T_RED_USER a LEFT JOIN T_P ...

  7. 慎用 Enum.GetHashCode()

    公司里遗留下了相当多的 Enum.GetHashCode()来获取枚举值的代码 但是这会产生装箱行为的!!因为Enum是值类型,GetHashCode()是Object的方法,调用GetHashCod ...

  8. c#中的23种设计模式

    C# 23种设计模式汇总 创建型模式 工厂方法(Factory Method) 在工厂方法模式中,工厂方法用来创建客户所需要的产品,同时还向客户隐藏了哪种具体产品类将被实例化这一细节.工厂方法模式的核 ...

  9. UIScrollview自动布局,UIScrollviewAutolayoutDemo

    参考文档:http://www.cocoachina.com/ios/20150104/10810.html UIScrollviewAutolayoutDemo地址:http://pan.baidu ...

  10. Oracle 客户端配置

    nstantclient-basic-nt-12.1.0.1.0\instantclient_12_1下面新建NETWORK文件夹,NETWORK下新建ADMIN文件夹,ADMIN下新建tnsname ...