如何在postgreSQL中删除差异小于10秒的重复数据

如何在postgreSQL中删除差异小于10秒的重复数据,postgresql,duplicates,sql-delete,Postgresql,Duplicates,Sql Delete,目前,该表如下所示 日期时间 数字 所容纳之物 2018-01-01 02:49:04 1. 春天 2018-01-01 02:49:10 1. 春天 2018-01-01 02:49:24 1. 春天 2018-01-01 02:49:29 1. 夏天 2018-01-01 02:49:44 1. 春天 2018-01-01 02:49:49 1. 春天 2018-01-01 02:49:50 1. 冬天 2018-01-01 02:49:51 1. 春天 假设表中的RECODR具有唯一的id

目前,该表如下所示

日期时间 数字 所容纳之物 2018-01-01 02:49:04 1. 春天 2018-01-01 02:49:10 1. 春天 2018-01-01 02:49:24 1. 春天 2018-01-01 02:49:29 1. 夏天 2018-01-01 02:49:44 1. 春天 2018-01-01 02:49:49 1. 春天 2018-01-01 02:49:50 1. 冬天 2018-01-01 02:49:51 1. 春天
假设表中的RECODR具有唯一的id-s,首先使用lag窗口函数查找要删除的id-s,然后按id将其删除。分两步执行此操作的原因是无法在where子句中使用窗口函数

以t作为 选择id, datetime-lagdatetime,按数字划分为1,按日期时间划分为内容顺序<间隔'10秒' 从表 从_表中删除,其中id在选择id from t where to_delete中;
假设表中的RECODR具有唯一的id-s,首先使用lag窗口函数查找要删除的id-s,然后按id将其删除。分两步执行此操作的原因是无法在where子句中使用窗口函数

以t作为 选择id, datetime-lagdatetime,按数字划分为1,按日期时间划分为内容顺序<间隔'10秒' 从表 从_表中删除,其中id在选择id from t where to_delete中;
我认为在一般情况下,窗口函数不能做到这一点

假设该表包含一系列时间戳,每秒一个。您对时间差的要求必须大于10秒,这意味着保留第一个时间戳,然后跳过接下来的9条记录,但下一条记录与第一条记录的时间差为10秒,因此应该保留。这意味着不能仅通过比较当前行和上一行来解决这一问题,而必须比较当前行和将保留的最后一行,如果差值小于10秒,则删除行,如果差值小于10秒,则保留行

所以。。。plpgsql

DROP TABLE foo;
CREATE TABLE foo( id SERIAL PRIMARY KEY,
dt TIMESTAMP WITHOUT TIME ZONE,
number INT NOT NULL, content TEXT NOT NULL);
\copy foo(dt,number,content) FROM stdin
2018-01-01 02:49:04 1   spring
2018-01-01 02:49:10 1   spring
2018-01-01 02:49:11 1   spring
2018-01-01 02:49:12 1   spring
2018-01-01 02:49:13 1   spring
2018-01-01 02:49:14 1   spring
2018-01-01 02:49:15 1   spring
2018-01-01 02:49:16 1   spring
2018-01-01 02:49:17 1   spring
2018-01-01 02:49:18 1   spring
2018-01-01 02:49:19 1   spring
2018-01-01 02:49:20 1   spring
2018-01-01 02:49:21 1   spring
2018-01-01 02:49:22 1   spring
2018-01-01 02:49:24 1   spring
2018-01-01 02:49:29 1   summer
2018-01-01 02:49:44 1   spring
2018-01-01 02:49:49 1   spring
2018-01-01 02:49:50 1   winter
2018-01-01 02:49:51 1   spring
\.

CREATE OR REPLACE FUNCTION foo_del( )
RETURNS SETOF INT
AS $$
DECLARE
    row         foo%ROWTYPE;
    last_row    foo%ROWTYPE;
BEGIN
    FOR row IN SELECT * FROM foo ORDER BY number, content, dt
    LOOP
        IF last_row.id IS NULL OR row.number != last_row.number OR row.content != last_row.content 
            OR row.dt >= last_row.dt THEN
            last_row := row;
            last_row.dt := last_row.dt + '10 SECOND'::INTERVAL;
        ELSE
            IF row.dt < last_row.dt THEN
                RETURN NEXT row.id;
            END IF;
        END IF;
    END LOOP;
END;
$$ LANGUAGE PLPGSQL;

SELECT * FROM foo LEFT JOIN (SELECT * FROM foo_del()) d ON (foo.id=d.foo_del) ORDER BY id;

DELETE FROM foo WHERE id IN (SELECT * FROM foo_del());

SELECT * FROM foo ORDER BY id;

我认为在一般情况下,窗口函数不能做到这一点

假设该表包含一系列时间戳,每秒一个。您对时间差的要求必须大于10秒,这意味着保留第一个时间戳,然后跳过接下来的9条记录,但下一条记录与第一条记录的时间差为10秒,因此应该保留。这意味着不能仅通过比较当前行和上一行来解决这一问题,而必须比较当前行和将保留的最后一行,如果差值小于10秒,则删除行,如果差值小于10秒,则保留行

所以。。。plpgsql

DROP TABLE foo;
CREATE TABLE foo( id SERIAL PRIMARY KEY,
dt TIMESTAMP WITHOUT TIME ZONE,
number INT NOT NULL, content TEXT NOT NULL);
\copy foo(dt,number,content) FROM stdin
2018-01-01 02:49:04 1   spring
2018-01-01 02:49:10 1   spring
2018-01-01 02:49:11 1   spring
2018-01-01 02:49:12 1   spring
2018-01-01 02:49:13 1   spring
2018-01-01 02:49:14 1   spring
2018-01-01 02:49:15 1   spring
2018-01-01 02:49:16 1   spring
2018-01-01 02:49:17 1   spring
2018-01-01 02:49:18 1   spring
2018-01-01 02:49:19 1   spring
2018-01-01 02:49:20 1   spring
2018-01-01 02:49:21 1   spring
2018-01-01 02:49:22 1   spring
2018-01-01 02:49:24 1   spring
2018-01-01 02:49:29 1   summer
2018-01-01 02:49:44 1   spring
2018-01-01 02:49:49 1   spring
2018-01-01 02:49:50 1   winter
2018-01-01 02:49:51 1   spring
\.

CREATE OR REPLACE FUNCTION foo_del( )
RETURNS SETOF INT
AS $$
DECLARE
    row         foo%ROWTYPE;
    last_row    foo%ROWTYPE;
BEGIN
    FOR row IN SELECT * FROM foo ORDER BY number, content, dt
    LOOP
        IF last_row.id IS NULL OR row.number != last_row.number OR row.content != last_row.content 
            OR row.dt >= last_row.dt THEN
            last_row := row;
            last_row.dt := last_row.dt + '10 SECOND'::INTERVAL;
        ELSE
            IF row.dt < last_row.dt THEN
                RETURN NEXT row.id;
            END IF;
        END IF;
    END LOOP;
END;
$$ LANGUAGE PLPGSQL;

SELECT * FROM foo LEFT JOIN (SELECT * FROM foo_del()) d ON (foo.id=d.foo_del) ORDER BY id;

DELETE FROM foo WHERE id IN (SELECT * FROM foo_del());

SELECT * FROM foo ORDER BY id;
您可以使用exists:

额外:如果您想消除中间人,但保留侧面记录,您可以使用: [这将始终保留第一个和最后一个记录,即使它们太接近]

您可以使用exists:

额外:如果您想消除中间人,但保留侧面记录,您可以使用: [这将始终保留第一个和最后一个记录,即使它们太接近]


您可以首先计算间隙小于10秒的所有行,并使用这些行的值删除它们。如果您有一个真正的唯一键,例如标识列,那么最好使用它,因为ctid比较非常慢

with flagged as (
  select ctid as rid, 
         row_number() over w as rn,
         datetime - lag(datetime) over w < interval '10 seconds' as small_gap
  from the_table
  window w as (partition by number, content order by datetime)
)
delete from the_table
where ctid in (select rid
               from flagged
               where small_gap)

您可以首先计算间隙小于10秒的所有行,并使用这些行的值删除它们。如果您有一个真正的唯一键,例如标识列,那么最好使用它,因为ctid比较非常慢

with flagged as (
  select ctid as rid, 
         row_number() over w as rn,
         datetime - lag(datetime) over w < interval '10 seconds' as small_gap
  from the_table
  window w as (partition by number, content order by datetime)
)
delete from the_table
where ctid in (select rid
               from flagged
               where small_gap)

我想这不会像预期的那样起作用。如果你有这样的系列:0:00:00,00:00:09,00:00:11,你会删除第二条和第三条记录,虽然你只需要删除第二条,但第一条和第三条之间的差异已经足够大了。很公平,你是对的。我想这不会像预期的那样起作用。如果您有这样的系列:0:00:00、00:00:09、00:00:11,您将删除第二条和第三条记录,尽管您只需要删除第二条记录,但第一条和第三条记录之间的差异足够大。很公平,您是对的。是否要递归应用此规则?因此,如果删除一行,使下一行和上一个幸存者行之间的差距变大,那么它是否也应该删除这些行?是否要递归应用此规则?因此,如果删除一行,使下一行和上一个幸存者行之间的差距变大,那么它是否也应该删除这些行?
with flagged as (
  select ctid as rid, 
         row_number() over w as rn,
         datetime - lag(datetime) over w < interval '10 seconds' as small_gap
  from the_table
  window w as (partition by number, content order by datetime)
)
delete from the_table
where ctid in (select rid
               from flagged
               where small_gap)