PostgreSQL的全部删除,但最久的记录(PostgreSQL delete all but t

2019-09-23 21:18发布

我有一个具有多个条目的PostgreSQL数据库objectid ,在多个devicenames ,但有一个唯一的timestamp每个条目。 该表看起来是这样的:

address | devicename | objectid      |  timestamp       
--------+------------+---------------+------------------------------
1.1.1.1 | device1    | vs_hub.ch1_25 | 2012-10-02 17:36:41.011629+00
1.1.1.2 | device2    | vs_hub.ch1_25 | 2012-10-02 17:48:01.755559+00
1.1.1.1 | device1    | vs_hub.ch1_25 | 2012-10-03 15:37:09.06065+00
1.1.1.2 | device2    | vs_hub.ch1_25 | 2012-10-03 15:48:33.93128+00
1.1.1.1 | device1    | vs_hub.ch1_25 | 2012-10-05 16:01:59.266779+00
1.1.1.2 | device2    | vs_hub.ch1_25 | 2012-10-05 16:13:46.843113+00
1.1.1.1 | device1    | vs_hub.ch1_25 | 2012-10-06 01:11:45.853361+00
1.1.1.2 | device2    | vs_hub.ch1_25 | 2012-10-06 01:23:21.204324+00

我要删除所有,但每个最早的条目odjectiddevicename 。 在这种情况下,我想删除所有,但:

1.1.1.1 | device1 | vs_hub.ch1_25 | 2012-10-02 17:36:41.011629+00
1.1.1.2 | device2 | vs_hub.ch1_25 | 2012-10-02 17:48:01.755559+00

有没有办法做到这一点? 或者是可以选择两个“最早的条目objectiddevicename ”到一个临时表?

Answer 1:

提炼所描述的结果,这将可能是最简单和最快的:

SELECT DISTINCT ON (devicename, objectid) *
FROM   tbl
ORDER  BY devicename, objectid, ts DESC;

详细资料及说明在此相关的答案 。

从你的样本数据,我的结论是你要删除原始表的大部分 。 这可能是更快地只是TRUNCATE表(或DROP和重建,因为你无论如何都应该添加一个代理键列),其余的行写入。 这也为您提供prestine表,隐含聚集(排序),这是最适合您的查询的方式,保存真空将有不这样做的工作。 而且它可能仍然较快整体:

我也强烈建议,以代理主键添加到您的表,最好是serial列。

BEGIN;

CREATE TEMP TABLE tmp_tbl ON COMMIT DROP AS
SELECT DISTINCT ON (devicename, objectid) *
FROM   tbl
ORDER  BY devicename, objectid, ts DESC;

TRUNCATE tbl;
ALTER TABLE tbl ADD column tbl_id serial PRIMARY KEY;

-- or, if you can afford to drop & recreate:
-- DROP TABLE tbl;
-- CREATE TABLE tbl (
--   tbl_id serial PRIMARY KEY
-- , address text
-- , devicename text
-- , objectid text
-- , ts timestamp);

INSERT INTO tbl (address, devicename, objectid, ts)
SELECT address, devicename, objectid, ts
FROM   tmp_tbl;

COMMIT;

做这一切在一个事务中,以确保你不会经历失败了一半。

这是快速的,只要你的设置temp_buffers是大到足以容纳临时表。 否则系统将开始数据交换到磁盘和性能需要潜水。 您可以设置temp_buffers只是这样当前会话:

SET temp_buffers = 1000MB;

所以你不要浪费RAM,你通常不会需要temp_buffers 。 必须是在会话第一次使用临时对象之前。 更多相关信息, 这个相关的答案 。

此外,由于INSERT遵循TRUNCATE一个事务中,会容易在预写日志 -提高性能。

考虑CREATE TABLE AS的替代路线:

  • 是什么原因导致大INSERT放缓和磁盘使用爆炸?

唯一的缺点:您需要在表上的排它锁 。 这可能是与重并发负载数据库问题。

最后,不要使用timestamp作为列名。 这是一个保留字在每一个SQL标准和PostgreSQL中类型名称。 我改名为列ts ,你可能已经注意到了。



Answer 2:

这应该这样做:

delete from devices
using (
   select ctid as cid, 
          row_number() over (partition by devicename, objectid order by timestamp asc) as rn
   from devices
) newest
where newest.cid = devices.ctid
and newest.rn <> 1;

它创建给最早的(具有最小的一个派生表,将分配唯一的号码(地址,设备名,OBJECTID)每个组合timestamp值)1号。然后,这个结果是用来删除所有那些不有数字1的虚拟列ctid是用于唯一地标识的那些行(它是由Postgres的供给的内部标识符)。

请注意,删除一个真正大量行,欧文的做法肯定会更快。

SQLFiddle演示: http://www.sqlfiddle.com/#!1/5d9fe/2



Answer 3:

DELETE FROM DEVICES d WHERE d.timestamp =(SELECT MIN(时间戳)从设备上WHERE OBJECTID = d.objectid和设备= d.device)



Answer 4:

我的建议是使用子查询,即检查与旧时间戳记录的所有脑干:

DELETE FROM tablename
WHERE EXISTS(
  SELECT * FROM tablename a
  WHERE tablenmae.address = a.address
    AND tablename.devicename = a.devicename
    AND tablename.objectid = a.objectid
    AND a.timestamp < tablename.timestamp
)

查询选择最久的记录将是这个样子:

SELECT address, devicename, objectid, MIN(timestamp)
FROM tablename
GROUP BY address, devicename, objectid


Answer 5:

这应该假设address, devicename and objectid组成一个唯一的标识符

DELETE FROM tablename 
WHERE 
  address || devicename || objectid || timestamp NOT IN 
  (SELECT 
     address || devicename || objectid || min(timestamp) 
   FROM tablename 
   GROUP BY address, devicename, objectid)

这将使用由唯一列到选择绑在一起一个连接字符串。 人们发现最小日期为独特的组合,下删除表中的记录。 也许不是最有效的,但它应该工作。



文章来源: PostgreSQL delete all but the oldest records