ChatGPT解决这个技术问题 Extra ChatGPT

如何在 PostgreSQL 中通过排序删除固定数量的行?

我正在尝试将一些旧的 MySQL 查询移植到 PostgreSQL,但我遇到了这个问题:

DELETE FROM logtable ORDER BY timestamp LIMIT 10;

PostgreSQL 不允许在其删除语法中进行排序或限制,并且该表没有主键,因此我不能使用子查询。此外,我想保留查询完全删除给定数字或记录的行为——例如,如果表包含 30 行但它们都具有相同的时间戳,我仍然想删除 10,尽管没关系哪个 10。

所以;如何在 PostgreSQL 中通过排序删除固定数量的行?

编辑:没有主键意味着没有 log_id 列或类似列。啊,遗留系统的乐趣!

为什么不添加主键? postgresql 中的小菜一碟:alter table foo add column id serial primary key
这是我最初的方法,但其他要求阻止了它。

m
mu is too short

您可以尝试使用 ctid

DELETE FROM logtable
WHERE ctid IN (
    SELECT ctid
    FROM logtable
    ORDER BY timestamp
    LIMIT 10
)

ctid 是:

行版本在其表中的物理位置。请注意,尽管 ctid 可用于非常快速地定位行版本,但如果行的 ctid 被 VACUUM FULL 更新或移动,它会发生变化。因此 ctid 作为长期行标识符是无用的。

还有 oid,但只有在创建表时特别要求时才存在。


这行得通,但它有多可靠?有什么我需要注意的“陷阱”吗?如果在查询运行时更改表中的 ctid 值,VACUUM FULL 或 autovacuum 是否可能会导致问题?
我不认为增量 VACUUM 不会改变 ctid。因为这只是在每个页面内压缩,而 ctid 只是行号而不是页面偏移量。 VACUUM FULL 或 CLUSTER 操作会更改 ctid,但这些操作首先会在表上获取访问排他锁。
@Whatsit:我对 ctid 文档的印象是,ctid 足够稳定,可以使这个 DELETE 工作正常,但不够稳定,例如,作为 ghetto-FK 放入另一个表。大概您不更新 logtable,因此您不必担心更改 ctidVACUUM FULL 确实会锁定表 (postgresql.org/docs/current/static/routine-vacuuming.html),因此您不必担心其他方式这ctid可以改变。 @araqnid 的 PostgreSQL-Fu 非常强大,文档同意他启动。
感谢你们两位的澄清。我确实查看了文档,但我不确定我是否正确解释了它们。在此之前我从未遇到过 ctids。
这实际上是一个非常糟糕的解决方案,因为 Postgres 无法在连接中使用 TID 扫描(IN 是它的一个特例)。如果你看这个计划,它应该是相当可怕的。因此,“非常快”仅在您明确指定 CTID 时适用。说的是版本10。
p
pd40

Postgres 文档建议使用数组而不是 IN 和子查询。这应该工作得更快

DELETE FROM logtable 
WHERE id = any (array(SELECT id FROM logtable ORDER BY timestamp LIMIT 10));

可以找到这个和其他一些技巧here


@Konrad Garus 给你link,“快速删除前 n 行”
@BlakeRegalia 不,因为指定的表中没有主键。这将删除前 10 个中具有“ID”的所有行。如果所有行具有相同的 ID,则所有行都将被删除。
如果 any (array( ... ));in ( ... ) 快,这听起来像是查询优化器中的错误 - 它应该能够发现该转换并对数据本身执行相同的操作。
我发现这种方法比在 UPDATE 上使用 IN 慢很多(这可能是不同的)。
测量 12 GB 表:第一次查询 450..1000 毫秒,第二次查询 5..7 秒:快速查询:从 cs_logging 中删除,其中 id = any(数组(从 cs_logging 中选择 id,其中 date_created < now() - 间隔 '1 天' * 30 and partition_key like '%I' order by id limit 500 )) 慢一:从 cs_logging where id in ( select id from cs_logging where date_created < now() - interval '1 days' * 30 and partition_key like '%我按 id 限制 500 订购)。使用 ctid 慢很多(分钟)。
K
Konrad Garus
delete from logtable where log_id in (
    select log_id from logtable order by timestamp limit 10);

u
user2449151

如果您没有主键,则可以将数组 Where IN 语法与复合键一起使用。

delete from table1 where (schema,id,lac,cid) in (select schema,id,lac,cid from table1 where lac = 0 limit 1000);

这对我有用。


P
Patrick Hüsler

假设您要删除任何 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)
B
Bernhard

您可以编写一个循环删除各个行的过程,该过程可以使用参数来指定要删除的项目数。但这与 MySQL 相比有点矫枉过正。