SQL语言 - 自连接

自连接意思就是表与其自身进行连接。虽然自连接并不是必须的,可以通过其他方式来完成,但是在某些情况下,使用自连接就比较方便。比如一张员工信息表,这张表上有该员工的基本信息,还有指明了他的上司的ID号。但是同时他的上司也是员工,在这张表上也有他的信息。如果我们需要显示出所有员工的姓名和他上司的姓名,虽然我们可以通过其他方式,比如使用子查询来完成这一任务,但是无疑使用自连接可能是最好的办法。比如下面这张表:

员工号码 员工姓名 上司号码
001 张三 003
002 李四 005
003 王五 009
004 赵六 003
005 吴七 010
006 周八 005
007 胡九  003
008 陈十 010
009 杨二 010
010 刘一  
          

现在我们要查询出每个员工的姓名以及他上司的姓名,如果不使用自连接那么我们就可以用下面的方式来完成该查询
select name 员工姓名
,(select name from employee mg where ep.managerid = mg.id) 上司姓名
from employee ep

查询结果:
 

员工姓名 上司姓名
张三 王五
李四 吴七
王五 杨二 
赵六 王五
吴七 刘一
周八 吴七
胡九 王五
陈十 刘一
杨二 刘一
刘一  

                
在这个查询语句中,使用了一个子查询来完成对上司姓名的查询。
如果使用自连接。那么结构会感觉清晰许多,如:
select ep.name 员工姓名,mg.name 上司姓名
from EMPLOYEE ep
LEFT JOIN EMPLOYEE mg ON ep.managerid = mg.id
这里除了使用自连接外还使用了左外连接,因为‘刘一’这个员工是所有人的上司,并且他没有其他的上司,也就是说他是老总了。如果用内连接就会把这行记录过滤掉,因为没有和他匹配的上司姓名。使用左外连接就可以避免这一情况。

查询结果:
 

员工姓名 上司姓名
胡九 王五
赵六 王五
张三  王五
周八 吴七
李四  吴七
王五 杨二
杨二 刘一
陈十 刘一
吴七 刘一
刘一  

               
查询结果都是一样的,只是最后显示出来的顺序不一样。

下面进入自连接的相关练习。
相关练习的数据库请参看:http://www.sqlzoo.cn/buses.htm
练习题地址:http://www.sqlzoo.cn/6.htm
练习题答案及分析:
1a. 显示数据库中包含多少车站.
SELECT COUNT(*) FROM stops

1b. 找出站名为'Craiglockhart'的id 号
SELECT id FROM stops WHERE name='Craiglockhart'

1c. 给出 'LRT'公司经营的'4'号线路的所有车站的id(站号)和name(站名)
SELECT id, name FROM stops, route
  WHERE id=stop
    AND company='LRT'
    AND num='4'

2a. 请看一下已经给出的查询,它将列出所有公交线路经过车站London Road (149)或Craiglockhart (53)的次数. 修改一下该查询,使得该查询仅列出两次经过这些车站的两条线路.
SELECT company, num, COUNT(*)
FROM route WHERE stop=149 OR stop=53
GROUP BY company, num
修改后:
SELECT company, num, COUNT(*)
FROM route WHERE stop=149 OR stop=53
GROUP BY company, num
HAVING COUNT(*)=2

2b. 执行下面的self join(自连接)查询,可以发现 b.stop 给出了所有从Craiglockhart(stop=53)出发可到达的站点. 请调整一下该查询,让它给从Craiglockhart(stop=53)到London Road(stop=149)的线路.
修改后:
SELECT a.company, a.num, a.stop, b.stop
FROM route a JOIN route b ON
  (a.company=b.company AND a.num=b.num)
WHERE a.stop = 149 AND b.stop=53
上面这道题就是使用了自连接,在使用自连接时,一定要给表加上别名,这样才能区分两张表。我们可以将自连接看成是两张不同的表,而其他连接的方式和多表连接没有什么不同,这样才能更容易的理解自连接。

2c. 下面的查询和上例中的比较类似.只是通过两次连接stops表,让我们可以通过站名而不是站号来查询公交线路.请调整一下该查询,让它可以显示'Craiglockhart'和'London Road'间的公交线路. 你也可以试试显示'Fairmilehead' 和 'Tollcross'间的公交线路.
修改后:
SELECT a.company, a.num, stopa.name, stopb.name
FROM route a JOIN route b ON
  (a.company=b.company AND a.num=b.num)
  JOIN stops stopa ON (a.stop=stopa.id)
  JOIN stops stopb ON (b.stop=stopb.id)
WHERE stopa.name='Craiglockhart'
  AND stopb.name='London Road'

3a. 给出连接车站号为115和137公交线路.
SELECT R1.company, R1.num
  FROM route R1, route R2
  WHERE R1.num=R2.num AND R1.company=R2.company
    AND R1.stop=115 AND R2.stop=137

3b. 给出连接车站 'Craiglockhart' and 'Tollcross'的公交线路.
SELECT R1.company, R1.num
  FROM route R1, route R2, stops S1, stops S2
  WHERE R1.num=R2.num AND R1.company=R2.company
    AND R1.stop=S1.id AND R2.stop=S2.id
    AND S1.name='Craiglockhart'
    AND S2.name='Tollcross'

3c. 给出可以从'Craiglockhart' 车站乘坐一辆汽车就能到达的车站.显示的结果包含到达车站号,车站名,公交线路的公司和公交线路号.
SELECT S2.id, S2.name, R2.company, R2.num
  FROM stops S1, stops S2, route R1, route R2
  WHERE S1.name='Craiglockhart'
    AND S1.id=R1.stop
    AND R1.company=R2.company AND R1.num=R2.num
    AND R2.stop=S2.id
 
3d. 给出从Sighthill 到 Craiglockhart的所有可能的交通线路.
这道题我是始终没做对。我找到了它的标准答案,贴上去还是有问题。不知道这道题是不是本身就有问题。希望哪位高人能帮我解决这个疑惑~下面贴出它的标准答案:
SELECT a.name Start_At, b.num FirstBus, b.company FBC,
       e.name Change_At, f.num SecondBus, f.company SBC,
       g.name Arive_At
 FROM  stops a, route b,
       stops c, route d,
       stops e, route f,
       stops g, route h
WHERE  a.id = b.stop                
  AND  c.id = d.stop
  AND  e.id = f.stop
  AND  g.id = h.stop
 
  AND  b.num = d.num
  AND  b.company = d.company  

  AND  f.num = h.num
  AND  f.company = h.company  
  AND  c.name = e.name

  AND  a.name = 'Craiglockhart'
  AND  g.name = 'Sighthill'

  AND d.pos > b.pos
  AND h.pos > f.pos
看起来有点恐怖吧,反正我是没辙了。。。

总结:其实SQL语言本身并不难,难点在于对数据库的理解上,因此我还是认为,学习数据库应该把重点放在数据库本身的设计上。

你可能感兴趣的:(数据库)