SQL进阶教程——自连接的用法(第二章)

1. 什么是自连接

针对相同的表进行的连接被称为“自连接”,一旦熟练掌握自连接技术,我们便能快速的解决很多问题。但是,其处理过程不太容易想象,以至于常常被人们敬而远之。
理解自连接不仅可以让我们学会实际工作中能用到的技能,还能增进我们对“面向集合”这一SQL语言特征的理解。

2. 可重排列,排列,组合

  • Products
name(商品名称) price(价格)
苹果 50
橘子 100
香蕉 80

假设这列有一张存放了商品名称及价格的表,表里有“苹果,橘子,香蕉”这三条记录。在生成用于查询销售额的报表等的时候,我们有时候需要获取这些商品的组合。
这里说的组合其实分为两种类型。一种是有顺序的有序对,另一种是无顺序的无序对。对应着学校里的术语“排列”和“组合”

--用于获取可重排列(有序对)的SQL语句
SELECT P1.name AS name_1,P2.name AS name_2
FROM Products P1,Products P2;--交叉连接生成笛卡尔积

--用于获取排列的SQL语句
SELECT P1.name AS name_1,P2.name AS name_2
FROM Products P1,Products P2
WHERE P1.name <> P2.name;

--用于生成组合的SQL语句
SELECT P1.name AS name_1,P2.name AS name_2
FROM Products P1,Products P2
WHERE P1.name > P2.name;

注意:

  1. 这里将非等值连接与自连接结合使用了,因此称为“非等值自连接”。在需要获取列的组合时,我们经常需要用到这个技术,请牢记。

3. 删除重复行

下面有一张商品表,现在假设在这张表里,“橘子”这种商品存在重复。可怕的是,这张表里连主键都没有(其实是根本无法设置主键)。我们需要马上清理一下,去掉重复行。

  • Products
name(商品名称) price(价格)
苹果 50
橘子 100
橘子 100
橘子 100
香蕉 80
--用于删除重复行的SQL语句(1):使用极值函数
DELETE FROM Products P1
WHERE rowid < (SELECT MAX(P2.rowid)
				FROM Products P2
				WHERE P1.name = P2.name
				AND P1.price = P2.price);
/**需要使用由数据库独自实现的行ID,这里的行ID可以理解成拥有“任何表都可以使用的主键”,
这里使用的Oracle数据库里的rowid
这个子查询会比较两个集合P1好P2,然后返回商品名称和价格都相同的行最大的rowid所在的行**/


--用于删除重复行的SQL语句(2):使用非等值连接
DELETE FROM Products P1
WHERE EXISTS (SELECT *
				FROM Products P2
				WHERE P1.name = P2.name
				AND P1.price = P2.price
				AND P1.rowid < P2.rowid);

4. 查找局部不一致的列

  • Addresses
name(姓名) family_id(家庭ID) address(地址)
前田一名 100 3-2-29
前田由美 100 3-2-92
加藤茶 200 2-8-1
加藤胜 200 2-8-1
福尔摩斯 300 221B
毕生 400 221B
--查找是同一家人但住址去不同的记录
SELECT DISTINCT A1.name,A1.address
	FROM Addresses A1,Addresses A2
	WHERE A1.family_id = A2.family_id
	AND A1.address <> A2.address;--自连接和非等值连接

5. 排序

  • Products
name(商品名称) price(价格)
苹果 50
橘子 100
葡萄 50
西瓜 80
柠檬 30
香蕉 50

现在我们要按照价格从高到低的顺序,对下面这张表的商品进行排序。

--排序:使用窗口函数
SELECT name,price
	RANK() OVER (ORDER BY price DESC) AS RANK_1,--会跳过位次
	DENSE_RANK() OVER(ORDER BY price DESC) AS RANK_2--不会跳过位次
FROM Products;
--排序从1开始,如果已出现相同位次,则跳过之后的位次
SELECT P1.name,
	   P2.price,
	   (SELECT COUNT(P2.price)
	   FROM Products P2
	   WHERE P2.price > p1.price) + 1 AS RANK_1
	   			--子查询所做的,是计算出价格比自己搞的记录的条数并将其作为自己的位次
FROM Products P1
GROUP BY RANK_1;
/**这段代码很容易扩展。例如去掉标量子查询后边的+1,就可以从0开始排序,
而且如果修改成COUNT(DISTINCT P2.price),那么存在相同位次的记录时,
就可以不跳过之后的位次,而是连续输出(相当于DENSE_RANK函数)**/

再次强调,在关联子查询中是信息流是双向的。外部查询的每行数据传递一个值给子查询,然后子查询为每一行数据执行一次并返回它的记录。然后,外部查询根据返回的记录做出决策。

--排序:使用自连接
SELECT P1.name,
	   MAX(P1.price) AS price,
	   COUNT(P2.name) +1 AS rank_1
FROM Products P1 LEFT OUTER JOIN Products P2
		--这里如果使用内连接,那么第一名“橘子”就会消失,因为没有比橘子价格更高的水果
 ON P1.price < p2.price
 GROUP BY P1.name
 ORDER BY rank_1;

本节要点:

  1. 自连接经常和非等值连接结合起来使用
  2. 自连接和GROUP BY结合使用可以生成递归集合
  3. 将自连接看做不同表之间的连接更容易理解
  4. 应把表看做行的集合,用面向集合的方法来思考
  5. 自连接的性能开销很大,应尽量给用于连接的列建立索引

你可能感兴趣的:(sql)