[PSQL] 自连接的用法

《SQL进阶教程》 MICK 1-2 自连接的用法

关于用于创建示例用表的SQL语句和示例代码等,请参考如下网站。
http://www.ituring.com.cn/book/1813(请点击“随书下载”下载中文版相关资料)

---------------------------------------------------------------

说明:如下笔记中的测试基于postgresql14

命令行连接本地PSQL:  psql -U <username> -d <dbname> -h 127.0.0.1 -W

目录

可重排列、排列、组合

删除重复行

方法一:使用极值函数

方法二:使用非等值连接

查找局部不一致的列

排序


可重排列、排列、组合

Products建表语句

CREATE TABLE Products
(name VARCHAR(16) PRIMARY KEY,
 price INTEGER NOT NULL);

--可重排列·排列·组合
INSERT INTO Products VALUES('苹果',	50);
INSERT INTO Products VALUES('橘子',	100);
INSERT INTO Products VALUES('香蕉',	80);

==========================================
test=> select * from Products
test-> ;
 name | price
------+-------
 苹果 |    50
 橘子 |   100
 香蕉 |    80
(3 rows)

test=>

针对相同的表进行的连接被称为“自连接”(self join)

(1)可重排列

test=> SELECT P1.name AS name_1, P2.name AS name_2
test-> FROM Products P1, Products P2;
 name_1 | name_2
--------+--------
 苹果   | 苹果
 苹果   | 橘子
 苹果   | 香蕉
 橘子   | 苹果
 橘子   | 橘子
 橘子   | 香蕉
 香蕉   | 苹果
 香蕉   | 橘子
 香蕉   | 香蕉
(9 rows)

test=>

(2)排列

去掉由相同元素构成的对,例如(苹果,苹果)

test=> SELECT P1.name AS name_1, P2.name AS name_2
test-> FROM Products P1, Products P2
test-> WHERE P1.name <> P2.name;
 name_1 | name_2
--------+--------
 苹果   | 橘子
 苹果   | 香蕉
 橘子   | 苹果
 橘子   | 香蕉
 香蕉   | 苹果
 香蕉   | 橘子
(6 rows)

test=>

(3)组合

test=> SELECT P1.name AS name_1, P2.name AS name_2
test-> FROM Products P1, Products P2
test-> WHERE P1.name > P2.name;
 name_1 | name_2
--------+--------
 苹果   | 橘子
 香蕉   | 橘子
 香蕉   | 苹果
(3 rows)

test=>

删除重复行

重建Products

test=> DROP TABLE Products;
DROP TABLE
test=>
test=> CREATE TABLE Products
test-> (rowid INTEGER PRIMARY KEY,
test(>  name VARCHAR(16),
test(>  price INTEGER NOT NULL);
CREATE TABLE
test=>
test=> INSERT INTO Products VALUES(1, '苹果',50);
INSERT 0 1
test=> INSERT INTO Products VALUES(2, '橘子',100);
INSERT 0 1
test=> INSERT INTO Products VALUES(3, '橘子',100);
INSERT 0 1
test=> INSERT INTO Products VALUES(4, '橘子',100);
INSERT 0 1
test=> INSERT INTO Products VALUES(5, '香蕉',80);
INSERT 0 1
test=>
test=> SELECT * FROM Products;
 rowid | name | price
-------+------+-------
     1 | 苹果 |    50
     2 | 橘子 |   100
     3 | 橘子 |   100
     4 | 橘子 |   100
     5 | 香蕉 |    80
(5 rows)

test=>

使用关联子查询删除重复行

方法一:使用极值函数

test=> DELETE FROM Products P1
test->  WHERE rowid < ( SELECT MAX(P2.rowid)
test(>                    FROM Products P2
test(>                   WHERE P1.name = P2. name
test(>                     AND P1.price = P2.price ) ;
DELETE 2
test=>
test=> SELECT * FROM Products;
 rowid | name | price
-------+------+-------
     1 | 苹果 |    50
     4 | 橘子 |   100
     5 | 香蕉 |    80
(3 rows)

test=>

方法二:使用非等值连接

test=> DELETE FROM Products P1
test->  WHERE EXISTS ( SELECT *
test(>                   FROM Products P2
test(>                  WHERE P1.name = P2.name
test(>                    AND P1.price = P2.price
test(>                    AND P1.rowid < P2.rowid );
DELETE 2
test=> SELECT * FROM Products;
 rowid | name | price
-------+------+-------
     1 | 苹果 |    50
     4 | 橘子 |   100
     5 | 香蕉 |    80
(3 rows)

test=>

查找局部不一致的列

【例】假设有下面这样一张住址表,主键是人名,同一家人家庭ID一样

如果家庭ID一样,住址也必须一样,可以认为前田夫妇这样的“同一家人但住址却不同的记录”是手误写错了,可以使用非等值自连接来实现找出这样的记录

Addresses建表语句

--查找局部不一致的列
CREATE TABLE Addresses
(name VARCHAR(32),
 family_id INTEGER,
 address VARCHAR(32),
 PRIMARY KEY(name, family_id));

INSERT INTO Addresses VALUES('前田义明', '100', '东京都港区虎之门3-2-29');
INSERT INTO Addresses VALUES('前田由美', '100', '东京都港区虎之门3-2-92');
INSERT INTO Addresses VALUES('加藤茶',   '200', '东京都新宿区西新宿2-8-1');
INSERT INTO Addresses VALUES('加藤胜',   '200', '东京都新宿区西新宿2-8-1');
INSERT INTO Addresses VALUES('福尔摩斯',  '300', '贝克街221B');
INSERT INTO Addresses VALUES('华生',  '400', '贝克街221B');

查找是同一家人但住址却不同的记录

test=> select * from Addresses;
   name   | family_id |         address
----------+-----------+-------------------------
 前田义明 |       100 | 东京都港区虎之门3-2-29
 前田由美 |       100 | 东京都港区虎之门3-2-92
 加藤茶   |       200 | 东京都新宿区西新宿2-8-1
 加藤胜   |       200 | 东京都新宿区西新宿2-8-1
 福尔摩斯 |       300 | 贝克街221B
 华生     |       400 | 贝克街221B
(6 rows)

test=>
test=> SELECT DISTINCT A1.name, A1.address
test-> FROM Addresses A1, Addresses A2
test-> WHERE A1.family_id = A2.family_id
test-> AND A1.address <> A2.address ;
   name   |        address
----------+------------------------
 前田义明 | 东京都港区虎之门3-2-29
 前田由美 | 东京都港区虎之门3-2-92
(2 rows)

test=>

【例】使用非等值自连接找出Products中价格相同的商品

test=> DROP TABLE Products;
DROP TABLE
test=> CREATE TABLE Products
test-> (name VARCHAR(16) PRIMARY KEY,
test(> price INTEGER NOT NULL);
CREATE TABLE
test=> DELETE FROM Products;
DELETE 0
test=> INSERT INTO Products VALUES('苹果',50);
INSERT 0 1
test=> INSERT INTO Products VALUES('橘子',100);
INSERT 0 1
test=> INSERT INTO Products VALUES('葡萄',50);
INSERT 0 1
test=> INSERT INTO Products VALUES('西瓜',80);
INSERT 0 1
test=> INSERT INTO Products VALUES('柠檬',30);
INSERT 0 1
test=> INSERT INTO Products VALUES('香蕉',50);
INSERT 0 1
test=>
test=> SELECT * FROM Products
test-> ;
 name | price
------+-------
 苹果 |    50
 橘子 |   100
 葡萄 |    50
 西瓜 |    80
 柠檬 |    30
 香蕉 |    50
(6 rows)

test=> SELECT DISTINCT P1.name, P1.price
test-> FROM Products P1, Products P2
test-> WHERE P1.price = P2.price
test-> AND P1.name <> P2.name;
 name | price
------+-------
 香蕉 |    50
 苹果 |    50
 葡萄 |    50
(3 rows)

test=>

排序

【例】按照价格从高到低对表Products里的商品进行排序。我们让价格相同的商品位次也一样,而紧接着它们的商品则有两种排序方法,一种是跳过之后的位次,另一种是不跳过之后的位次。

test=> SELECT name, price,
test->     RANK() OVER (ORDER BY price DESC) AS rank_1,
test->     DENSE_RANK() OVER (ORDER BY price DESC) AS rank_2
test->   FROM Products;
 name | price | rank_1 | rank_2
------+-------+--------+--------
 橘子 |   100 |      1 |      1
 西瓜 |    80 |      2 |      2
 苹果 |    50 |      3 |      3
 葡萄 |    50 |      3 |      3
 香蕉 |    50 |      3 |      3
 柠檬 |    30 |      6 |      4
(6 rows)

test=>

【例】不依赖于排序函数实现,如果已出现相同位次,则跳过之后的位次

test=> SELECT P1.name,
test->        P1.price,
test->       (SELECT COUNT(P2.price)
test(>          FROM Products P2
test(>         WHERE P2.price > P1.price) + 1 AS rank_1
test->  FROM Products P1
test->  ORDER BY rank_1;
 name | price | rank_1
------+-------+--------
 橘子 |   100 |      1
 西瓜 |    80 |      2
 苹果 |    50 |      3
 葡萄 |    50 |      3
 香蕉 |    50 |      3
 柠檬 |    30 |      6
(6 rows)

test=>

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值