dar*_*s33 5 sql postgresql duplicate-removal
我有一个具有多个条目的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
Run Code Online (Sandbox Code Playgroud)
我想删除每个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
Run Code Online (Sandbox Code Playgroud)
有办法吗?或者是否可以在临时表中为“ objectid和devicename” 选择最早的条目?
应该这样做:
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;
Run Code Online (Sandbox Code Playgroud)
它创建一个派生表,该表将为(地址,设备名,对象ID)的每种组合分配唯一的编号,并为最早的(timestamp值最小的那个)组合赋予编号1。然后使用此结果删除所有不具有编号的组合数字1。虚拟列ctid用于唯一标识那些行(这是Postgres提供的内部标识符)。
请注意,对于删除非常多的行,Erwin的方法无疑将更快。
SQLFiddle演示:http ://www.sqlfiddle.com/#!1/5d9fe /2
要提取所描述的结果,这可能是最简单且最快的:
SELECT DISTINCT ON (devicename, objectid) *
FROM tbl
ORDER BY devicename, objectid, ts DESC;
Run Code Online (Sandbox Code Playgroud)
详细信息和解释在此相关答案中。
根据您的示例数据,我得出结论,您将删除原始表的大部分内容。仅TRUNCATE表(或DROP重新创建,因为无论如何您都应该添加代理 pk 列)并将剩余的行写入其中可能会更快。这还为您提供了一个原始的表,以最适合您的查询的方式隐式集群(排序),并节省了必须VACUUM执行的工作。总体而言,它可能仍然更快:
我还强烈建议在表中添加代理主键,最好是一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;
Run Code Online (Sandbox Code Playgroud)
在一个事务中完成这一切,以确保您不会中途失败。
只要您的设置temp_buffers足够大以容纳临时表,这个速度就很快。否则系统将开始将数据交换到磁盘,并且性能会下降。您可以temp_buffers仅针对当前会话进行设置,如下所示:
SET temp_buffers = 1000MB;
Run Code Online (Sandbox Code Playgroud)
因此,您不会浪费通常不需要的 RAM temp_buffers。必须在会话中首次使用任何临时对象之前进行设置。更多信息请参阅此相关答案。
另外,如下INSERT所示在TRUNCATE事务内部,将很容易在Write Ahead Log上- 提高性能。
考虑CREATE TABLE AS替代路线:
唯一的缺点:您需要表上的独占锁。这在并发负载很重的数据库中可能会出现问题。
最后,切勿用作timestamp列名。它是每个 SQL 标准中的保留字,也是 PostgreSQL 中的类型名称。我用ts的是。
| 归档时间: |
|
| 查看次数: |
3679 次 |
| 最近记录: |