SQL查询入门(中篇)

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

多表连接简介

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

  因为连接(join)就是一种把多个表连接成一个表的重要手段。

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

 

进行连接后如图:

笛卡尔积

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

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

内连接

   如果分步骤理解的话,内连接可以看出先对两个表进行交叉连接后,在通过加上限制条件(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] s
inner join
[Student] c
on s.ClassID>s.StudentClassID

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

虽然上面链接后的表并没有什么实际意义,但这里仅作实验之用

  关系演算

    上面笛卡尔积的概念是关系代数中的概念,而我在第一篇文章中提到还有关系演算的查询方式,上面关系代数是分布式理解的,上面的语句推导过程是这样的:"对表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  * 
from [student] s
left outer join ClassTb c
on s.ClassID=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 * 
from [Employee] s
inner join [Employee] c
on s.ManagerID=c.ManagerID and s.EmployeeName='Careyson'

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

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

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

 再一看很让人迷糊是吧,也许你看到这一句脑海中第一印象会是:

SELECT  EmployeeName
FROM [fordemo].[dbo].[MeettingRecord] m
where MeetingName='¨谈论项目进度¨' and meetingName='讨论职业发展'

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

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

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

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

 

SELECT  m.EmployeeName
FROM [fordemo].[dbo].[MeettingRecord] m,
[fordemo].[dbo].[MeettingRecord] m2
where m.MeetingName='谈论项目进度' and m2.MeetingName='讨论职业发展'
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查询所必不可少的!

 

posted @ 2012-01-02 18:36  指尖流淌  阅读(704)  评论(0编辑  收藏  举报