1-2 自连接的用法
可重排列、排列、组合
应用场景:假设一张存放了商品名称及价格的表,表里有“苹果、橘子、香蕉”三条记录。在生成用于查询销售额的报表时,需要获取这些商品的组合。
组合:分为有顺序的有序对 <1,2> 和 无顺序的无序对 {1,2}。分别对应“排列”和“组合”
(1)可重排列
通过交叉连接生成笛卡尔积,可以得到有序对
/* 用于获取可重排列的SQL语句 */
SELECT P1.name AS name_1, P2.name AS name_2
FROM Products P1, Products P2;
(2)排列
去掉由相同元素构成的对。
/* 用于获取排列的SQL语句 */
SELECT P1.name AS name_1, P2.name AS name_2
FROM Products P1, Products P2
WHERE P1.name <> P2.name;
(3)组合
对调换了元素顺序的对进行去重
/* 用于获取组合的SQL语句 */
SELECT P1.name AS name_1, P2.name AS name_2
FROM Products P1, Products P2
WHERE P1.name > P2.name;
若想要获取3个以上元素的组合,只需要简单扩展一下即可
/* 用于获取组合的SQL语句:扩展成3列时 */
SELECT P1.name AS name_1, P2.name AS name_2, P3.name AS name_3
FROM Products P1, Products P2, Products P3
WHERE P1.name > P2.name
AND P2.name > P3.name;
删除重复行
(1)使用极值函数
/* 用于删除重复行的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 )
SQL中赋予不同名称的集合,我们应该将其看作不同的集合,这个子查询会比较P1、P2,然后返回商品名称和价格都相同的行里最大的rowid所在的行。
(2)使用非等值连接
/* 用于删除重复行的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 );
查找局部不一致的列
场景:查找“同一家人但是住址不同的记录”
自连接与非等值连接结合
/* 用于查找是同一家人但住址却不同的记录的SQL语句 */
SELECT DISTINCT A1.name, A1.address
FROM Addresses A1, Addresses A2
WHERE A1.family_id = A2.family_id
AND A1.address <> A2.address ;
问题:查找价格相等的商品组合
思路:
家庭——>价格
住址——>商品名称
排序
应用场景:在使用数据库制作各种票据和统计表工作中,会经常遇到按分数、人数或者销售额等数值进行排序的需求。(Oracle 、DB2 数据库的rank函数)
(1)使用窗口函数
/* 排序:使用窗口函数 */
SELECT name, price,
RANK() OVER (ORDER BY price DESC) AS rank_1,
DENSE_RANK() OVER (ORDER BY price DESC) AS rank_2
FROM Products;
出现相同位次后,rank_1跳过之后的位次,rank_2没有跳过,而是连续排序。
(2)SQL 和 集合论的关系
/* 排序从1开始。如果已出现相同位次,则跳过之后的位次 */
SELECT P1.name,
P1.price,
(SELECT COUNT(P2.price)
FROM Products P2
WHERE P2.price > P1.price) + 1 AS rank_1
FROM Products P1
ORDER BY rank_1;
/* 排序从1开始,连续输出位次 */
SELECT P1.name,
P1.price,
(SELECT COUNT( DISTINCT P2.price)
FROM Products P2
WHERE P2.price > P1.price) + 1 AS rank_1
FROM Products P1
ORDER BY rank_1;
执行原理:面向集合的思维方式。子查询所做的,是计算出价格比自己高的记录并将其作为自己的位次。
集合 | 价格 | 比自己高的价格 | 位次 |
---|---|---|---|
S0 | 100 | 0 | |
S1 | 80 | 100 | 1 |
S1 | 50 | 100,80 | 2 |
S2 | 30 | 100,80,50 | 3 |
0=空
1={0}
2={0,1}
3={0,1,2}
(3)使用自连接
/* 排序:使用自连接 */
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;