删除与 MySQL 中的上一行没有区别的行

Remove rows that do not differ from the previous row in MySQL

假设我有一个 table 来记录我的数据库随时间的变化:

TimeOfChange FieldA FieldB FieldC
-------------------------------------
2019-01-01     A1     B1     C1       /*(R1)*/
2019-01-02     A2     B2     C1       /*(R2)*/
2019-01-03     A2     B2     C1       /*(R3)*/
2019-01-05     A1     B1     C2       /*(R4)*/
2019-01-07     A1     B1     C1       /*(R5)*/

我的数据库有很多行没有发生重大变化,例如行 (R3) 与 (R2) 相同。 我想删除这些行。我找到了很多关于如何使用通用 table 表达式从 table 中删除重复行的参考资料。因此可以删除重复的(忽略 TimeOfChange 列)行。但这也将删除 (R5),因为它与 R1 相同。当按 TimeOfChange 列排序时,我只想删除与前一行具有相同 ABC 值的行。我该怎么做?

编辑:您可以假设 TimeOfChange 值都是唯一的

假设 TimeOfChange 是唯一的,你可以这样做:

delete
from data
where TimeOfChange in (
  select TimeOfChange
  from (
    select d2.TimeOfChange
    from data d1
      join data d2
    where d2.TimeOfChange in (
      select min(x.TimeOfChange) 
      from data x
      where x.TimeOfChange>d1.TimeOfChange
    ) and d1.FieldA=d2.FieldA and d1.FieldB=d2.FieldB and d1.FieldC=d2.FieldC
  ) as q
);

因此您首先要确定哪些行是 "next",然后检查 "next" 是否与 "current" 具有相同的值。对于那些 "next" 将形成一个结果集,你想在 DELETE 中使用。 select * from data 是为了避免在 DELETE 和子查询中重用 table。

如果将逻辑分离到存储过程中并将要删除的行的 ID 存储到临时文件中,您可能会获得更好的性能 table。

DB Fiddle

假设您的意思是 "when the same A, B, C occurred on the most recent day prior that had any data",这应该可用于识别需要删除的行:

SELECT t2.TimeOfChange, t2.FieldA, t2.FieldB, t2.FieldC
FROM (
   SELECT tMain.TimeOfChange, tMain.FieldA, tMain.FieldB, tMain.FieldC
      , MAX(tPrev.TimeOfChange) AS prevTimeOfChange
   FROM t AS tMain
   LEFT JOIN t AS tPrev ON t.TimeOfChange> tPrev.TimeOfChange
   GROUP BY tMain.TimeOfChange, tMain.FieldA, tMain.FieldB, tMain.FieldC
) AS t2
INNER JOIN t AS tPrev2 
   ON t2.prevTimeOfChange = tPrev2.TimeOfChange
   AND t2.FieldA = tPrev2.FieldA 
   AND t2.FieldB = tPrev2.FieldB 
   AND t2.FieldC = tPrev2.FieldC

这可以在 DELETE 中使用,通过一些间接的方式强制创建一个临时文件 table。

DELETE td 
FROM t AS td 
WHERE (td.TimeOfChange, td.FieldA, td.FieldB, td.FieldC) 
  IN (SELECT * FROM ([the query above]) AS tt) -- Yes, you have to wrap the query from above in a select * so mysql will not reject it.
;

然而,在走到这一步之后,会发生什么......

2019-01-01     A1     B1     C1
2019-01-02     A2     B2     C1
2019-01-03     A2     B2     C1
2019-01-04     A1     B1     C2
2019-01-05     A1     B1     C3
2019-01-05     A1     B1     C1
2019-01-06     A1     B1     C3
2019-01-07     A1     B1     C1

变成

2019-01-01     A1     B1     C1
2019-01-02     A2     B2     C1
2019-01-04     A1     B1     C2
2019-01-05     A1     B1     C3
2019-01-05     A1     B1     C1
2019-01-07     A1     B1     C1

现在是否需要进行第二遍删除 2019-01-07 条目? 您要重复 运行 查询直到没有行受到影响吗?