我有一个具有多个条目的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
我要删除所有,但每个最早的条目odjectid
和devicename
。 在这种情况下,我想删除所有,但:
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
有没有办法做到这一点? 或者是可以选择两个“最早的条目objectid
和devicename
”到一个临时表?
提炼所描述的结果,这将可能是最简单和最快的:
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
的替代路线:
唯一的缺点:您需要在表上的排它锁 。 这可能是与重并发负载数据库问题。
最后,不要使用timestamp
作为列名。 这是一个保留字在每一个SQL标准和PostgreSQL中类型名称。 我改名为列ts
,你可能已经注意到了。
这应该这样做:
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
DELETE FROM DEVICES d WHERE d.timestamp =(SELECT MIN(时间戳)从设备上WHERE OBJECTID = d.objectid和设备= d.device)
我的建议是使用子查询,即检查与旧时间戳记录的所有脑干:
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
这应该假设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)
这将使用由唯一列到选择绑在一起一个连接字符串。 人们发现最小日期为独特的组合,下删除表中的记录。 也许不是最有效的,但它应该工作。