我正在尝试将一些旧的 MySQL 查询移植到 PostgreSQL,但我遇到了这个问题:
DELETE FROM logtable ORDER BY timestamp LIMIT 10;
PostgreSQL 不允许在其删除语法中进行排序或限制,并且该表没有主键,因此我不能使用子查询。此外,我想保留查询完全删除给定数字或记录的行为——例如,如果表包含 30 行但它们都具有相同的时间戳,我仍然想删除 10,尽管没关系哪个 10。
所以;如何在 PostgreSQL 中通过排序删除固定数量的行?
编辑:没有主键意味着没有 log_id
列或类似列。啊,遗留系统的乐趣!
alter table foo add column id serial primary key
。
您可以尝试使用 ctid
:
DELETE FROM logtable
WHERE ctid IN (
SELECT ctid
FROM logtable
ORDER BY timestamp
LIMIT 10
)
ctid
是:
行版本在其表中的物理位置。请注意,尽管 ctid 可用于非常快速地定位行版本,但如果行的 ctid 被 VACUUM FULL 更新或移动,它会发生变化。因此 ctid 作为长期行标识符是无用的。
还有 oid
,但只有在创建表时特别要求时才存在。
Postgres 文档建议使用数组而不是 IN 和子查询。这应该工作得更快
DELETE FROM logtable
WHERE id = any (array(SELECT id FROM logtable ORDER BY timestamp LIMIT 10));
可以找到这个和其他一些技巧here
any (array( ... ));
比 in ( ... )
快,这听起来像是查询优化器中的错误 - 它应该能够发现该转换并对数据本身执行相同的操作。
UPDATE
上使用 IN
慢很多(这可能是不同的)。
delete from logtable where log_id in (
select log_id from logtable order by timestamp limit 10);
如果您没有主键,则可以将数组 Where IN 语法与复合键一起使用。
delete from table1 where (schema,id,lac,cid) in (select schema,id,lac,cid from table1 where lac = 0 limit 1000);
这对我有用。
假设您要删除任何 10 条记录(没有排序),您可以这样做:
DELETE FROM logtable as t1 WHERE t1.ctid < (select t2.ctid from logtable as t2 where (Select count(*) from logtable t3 where t3.ctid < t2.ctid ) = 10 LIMIT 1);
对于我的用例,删除 10M 记录,结果证明速度更快。
delete from "table" where ("table_ID") in (select "table_ID" from "table" limit 10)
您可以编写一个循环删除各个行的过程,该过程可以使用参数来指定要删除的项目数。但这与 MySQL 相比有点矫枉过正。
ctid
值,VACUUM FULL
或 autovacuum 是否可能会导致问题?ctid
文档的印象是,ctid
足够稳定,可以使这个 DELETE 工作正常,但不够稳定,例如,作为 ghetto-FK 放入另一个表。大概您不更新logtable
,因此您不必担心更改ctid
和VACUUM FULL
确实会锁定表 (postgresql.org/docs/current/static/routine-vacuuming.html),因此您不必担心其他方式这ctid
可以改变。 @araqnid 的 PostgreSQL-Fu 非常强大,文档同意他启动。