23.1 标量子查询比较

找出比学生大一岁以上的老师

  1. SELECT t1.teacher_name
  2. from teachers as t1
  3. where t1.birth_date >
  4. (select max(s1.birth_date) - INTERVAL '365' day FROM student as s1);

这个例子中,标量子查询将只运行一次,并且被优化器扫描Teachers表前缩减为一个常量值。

27.1.4 按顺序分区

  1. CREATE table paymenthistory(
  2. payment_nbr INTEGER not NULL PRIMARY key,
  3. paid_on_time char(1) DEFAULT 'Y' not NULL CHECK (paid_on_time in ('Y','N'))
  4. );
  5. INSERT INTO paymenthistory VALUES(1006,'Y'),
  6. (1005,'Y'),
  7. (1004,'N'),
  8. (1003,'Y'),
  9. (1002,'Y'),
  10. (1001,'Y'),
  11. (1000,'N');
  12. SELECT
  13. ( SELECT COUNT(*) FROM paymenthistory AS H2, paymenthistory AS H3
  14. WHERE H3.payment_nbr = H2.payment_nbr + 1
  15. AND H3.paid_on_time <> H2.paid_on_time AND
  16. H2.payment_nbr >= H1.payment_nbr )+ 1 AS grp,
  17. payment_nbr,
  18. paid_on_time
  19. FROM
  20. paymenthistory AS h1;
  1. 为每个及时或者延迟支付组分配组号。

27.2 关系除法

关系除法。思路是利用除数表对被除数表进行分区,并生成商或结果。
这问题用一个例子可以解释清楚。假设一张表记录了飞行员以及他们可以驾驶的飞机(被除数),有一张表记录了飞机库中的飞机(除数),我们想要一份可以驾驶飞机库中所有飞机的飞行员名单(商)。

  1. create table PilotSkills
  2. ( pilot char ( 15 ) not null,
  3. plane char ( 15 ) not null,
  4. primary key ( pilot , plane ));
  5. insert into PilotSkills
  6. values ( 'Celko' , 'Piper Cub' ),
  7. ( 'Higgins' , 'B-52 Bomber' ),
  8. ( 'Higgins' , 'F-14 Fighter' ),
  9. ( 'Higgins' , 'Piper Cub' ),
  10. ( 'Jones' , 'B-52 Bomber' ),
  11. ( 'Jones' , 'F-14 Fighter' ),
  12. ( 'Smith' , 'B-1 Bomber' ),
  13. ( 'Smith' , 'B-52 Bomber' ),
  14. ( 'Smith' , 'F-14 Fighter' ),
  15. ( 'Wilson' , 'B-1 Bomber' ),
  16. ( 'Wilson' , 'B-52 Bomber' ),
  17. ( 'Wilson' , 'F-14 Fighter' ),
  18. ( 'Wilson' , 'F-17 Fighter' );
  19. create table Hangar ( plane char ( 15 ) primary key );
  20. insert into Hangar
  21. values ( 'B-1 Bomber' ),
  22. ( 'B-52 Bomber' ),
  23. ( 'F-14 Fighter' );
  1. 在该例中 SmithWilson是两个能够驾驶飞机库中所有飞机的飞行员。Higgins Celko能驾驶Piper Cub飞机,但是正好没有此飞机。<br />关系除法的重要特征是除数和商的CROSS JOIN将产生与一个有效的被除数的行子集。

27.2.1 带余除法

带余数除法允许被除数表中包含的值比除数多。例如,如果一个飞行员可以驾驶比飞机库中飞机种类还要多久的飞机,这对我们是有利的,查询如下。

  1. SELECT distinct pilot
  2. from PilotSkills as ps1
  3. WHERE not EXISTS (SELECT *
  4. from Hangar
  5. where not EXISTS (SELECT *
  6. FROM PilotSkills as ps2
  7. where (ps1.pilot = ps2.pilot)
  8. and ps2.plane = hangar.plane))
  1. 另外一种避免三层嵌套的写法
  1. SELECT ps1.pilot
  2. FROM PilotSkills as ps1, Hangar as h1
  3. WHERE ps1.plane = h1.plane
  4. GROUP BY ps1.pilot
  5. HAVING count(ps1.plane) = (SELECT count(plane) FROM hangar)
  1. 上面两种存在差别,如果飞机库中没有数据,NOT EXISTS 所有飞行员都将从对空集的除法中返回。

27.2.2 精确除法

被除数表必须与除数值精确匹配。不允许有任何额外的值。

  1. select ps1.pilot
  2. FROM PilotSkills as ps1
  3. left outer join hangar as h1
  4. on ps1.plane = h1.plane
  5. group by ps1.pilot
  6. HAVING count(ps1.plane) = (SELECT count(plane) FROM hangar)
  7. and count(h1.plane) = (SELECT count(plane) from hangar)
  1. 此查询规定一个飞行员必须拥有与飞机库中飞机数量相同的执照,并且这些执照应与飞机库中的飞机完全匹配,不能匹配别的东西。

27.2.4 Todd的除法

  1. create table JobParts (job_nbr char(2), part_nbr char(2));
  2. insert into JobParts
  3. values ('j1' 'p1' ),
  4. ( 'j1' 'p2' ),
  5. ( 'j2' 'p2' ),
  6. ( 'j2' 'p4' ),
  7. ( 'j2' 'p5' ),
  8. ( 'j3' 'p2' ) ;

27.2.5 带JOIN的除法

  1. SELECT SP1.Pilot
  2. FROM (((SELECT plane FROM Hangar) AS H1
  3. INNER JOIN
  4. (SELECT pilot , plane FROM PilotSkills) AS SP1
  5. ON H1.plane = SP1.plane )
  6. INNER JOIN (SELECT *
  7. FROM PilotSkills
  8. WHERE pilot = 'Higgins') AS H2
  9. ON H2.plane = H1.plane )
  10. GROUP BY SP1.Pilot
  11. HAVING COUNT(*) >= (SELECT COUNT(*)
  12. FROM PilotSkills
  13. WHERE pilot = 'Higgins');
  14. SELECT * from PilotSkills ;