【问题标题】:Delete duplicate rows of a table based on the value of the previous row根据上一行的值删除表的重复行
【发布时间】:2021-12-31 00:21:09
【问题描述】:

假设我有一个这样的 SQL 表:

serial(pk) name status location time_updated
1 'joe' 'at_home' 'USA' 01:30
2 'jane' 'at_store' 'USA' 02:30
3 'joe' 'driving' 'USA' 12:15
4 'joe' 'driving' 'USA' 13:30
5 'joe' 'at_store' 'USA' 15:00
5 'joe' 'at_store' 'USA' 15:15
6 'joe' 'driving' 'USA' 16:00
7 'joe' 'driving' 'USA' 17:10
8 'joe' 'at_home' 'USA' 20:00

在这个表中可以有三种不同的状态':“at_home”、“at_store”和“driving”。

我想要每个人按时间顺序排列的动作。例如,对于 joe,这看起来像 at_home ->driving ->driving -> at_store -> at_store ->driving ->driving -> at_home

但是,我想删除任何重复的“驾驶”状态,只保留最早的。例如,对于 joe,这看起来像 at_home ->driving -> at_store -> at_store ->driving -> at_home。我不想删除重复的“at_home”或“at_store”

在本例中,我想从 12:15 开始保持“驾驶”状态,并在 16:00 保持“驾驶”状态,同时删除后面的重复状态。

我想专门为每个人执行此操作,因此当我执行“按 time_updated 排序”时,我可以按顺序查看该人的所有条目。

如果我使用"select * from db where name = 'joe' order by time_updated" 查询结果表,我的理想结果是:

serial(pk) name status location time_updated
1 'joe' 'at_home' 'USA' 01:30
3 'joe' 'driving' 'USA' 12:15
5 'joe' 'at_store' 'USA' 15:00
5 'joe' 'at_store' 'USA' 15:15
6 'joe' 'driving' 'USA' 16:00
8 'joe' 'at_home' 'USA' 20:00

有没有办法在 postgres 中做到这一点?

谢谢

【问题讨论】:

    标签: sql postgresql


    【解决方案1】:

    您不需要删除带有status = 'driving'(name)的连续行,您可以使用此查询将它们从结果中排除(使用window function LAG()):

    WITH sq AS (SELECT *,
                       CASE WHEN status = 'driving' AND status = LAG(status) OVER (PARTITION BY name ORDER BY time_updated) THEN 1 ELSE 0 END contiguous_driving
                FROM t)
                
    SELECT * 
    FROM sq
    WHERE name = 'joe' AND contiguous_driving = 0
    ORDER BY time_updated;
    

    如果你仍然想删除它们,你可以使用相同的子查询:

    WITH sq AS (SELECT *,
                       CASE WHEN status = 'driving' AND status = LAG(status) OVER (PARTITION BY name ORDER BY time_updated) THEN 1 ELSE 0 END contiguous_driving
                FROM t)
                
    DELETE FROM t
    WHERE serial IN (SELECT serial
                     FROM sq 
                     WHERE contiguous_driving = 1);
    

    如果serial列为主键,则不能有重复值

    【讨论】:

      【解决方案2】:

      您可以将删除与存在逻辑一起使用:

      DELETE
      FROM yourTable t1
      WHERE status = 'driving' AND
            NOT EXISTS (SELECT 1 FROM yourTable t2
                        WHERE t2.name = t1.name AND
                              t2.time_updated < t1.time_updated);
      

      【讨论】:

        【解决方案3】:

        首先,您获取结果并按日期倒序排列以获取最后一行,您甚至可以将此搜索限制为其中几个或仅最后一行。

        SELECT * FROM table ORDER BY time_updated DESC LIMIT 1;
        

        那么如果状态是'正在驾驶'

        UPDATE table SET time_updated = currenttime WHERE id = currentID
        

        您可能可以直接在 postgres 上使用触发器和过程自动执行此操作,但这超出了这个问题

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-04-21
          • 2019-05-29
          • 2018-07-15
          • 1970-01-01
          • 2021-05-13
          • 2020-11-24
          • 2021-06-24
          • 1970-01-01
          相关资源
          最近更新 更多