作者:缪晓丽
DBA、数据库爱好者、从业10年。对 DB2、PostgreSQL、Oracle 均有较长的运维经验。
本次演示的 test 表如下:
01
Oracle 的 rowid
SQL 语句:
DELETE FROM (
SELECT ROW_NUMBER() OVER (PARTITION BY id ORDER BY id) AS rn, id, name
FROM test
)
WHERE t.rn = 2
结果:删除不成功。原因:delete from 后的子查询就是视图,视图是不支持 DML 操作的。
此时 rowid 派上用场,rowid 是一个伪列,并不实际存储在表中,也不占用物理数据文件,它只是标记表中每条数据的物理位置。
格式如下:
第一部分 6 位表示:该行数据所在的数据对象的 data_object_id;
第二部分 3 位表示:该行数据所在的相对数据文件的 id;
第三部分 6 位表示:该数据行所在的数据块的编号;
第四部分 3 位表示:该行数据的行的编号;
可通过 dbms_rowid 系列函数查看 ROWID 的相关信息。
用 ROWID 代替 ID 排序。
SQL 语句:
DELETE FROM test
WHERE rowid = (
SELECT ROWID
FROM (
SELECT ROW_NUMBER() OVER (PARTITION BY ID ORDER BY rowid) AS RN, ID
FROM TEST
)
WHERE RN = 1
);
结果:删除成功。
02
PostgreSQL 的 CTID
PostgreSQL 并没用 ROWID 伪列,但是有 ctid。
ctid 的结构较 rowid 简单,如下:
格式:(Data block, Row);
Data block: 记录所在的数据块编号;
Row: 记录的行编号。
其中 0 代表第 0 块,1 和 2 代表第 1 行和第 2 行。
删除思路相同,且 PostgreSQL 支持窗口函数。
SQL 语句:
DELETE FROM test
WHERE ctid = (
SELECT ctid
FROM (
SELECT ROW_NUMBER() OVER (PARTITION BY ID ORDER BY ctid) AS rn, id, ctid
FROM TEST
)
WHERE rn = 1
);
但是删除时报错了:
子查询需要加别名。
DELETE FROM test
WHERE ctid = (
SELECT t.ctid
FROM (
SELECT ROW_NUMBER() OVER (PARTITION BY ID ORDER BY ctid) AS rn, id, ctid
FROM TEST
) t
WHERE t.rn = 1
);
结果:删除成功。
长期征稿·
恩墨学院公众号开启长期征稿,只要你有好的技术相关文章,欢迎投稿到:
edu_mkt@enmotech.com
有机会获得恩墨学院周边奖励、恩墨学院精品课程以及培训优惠卷。