Удалить строки, которые не отличаются от предыдущей строки в MySQL - PullRequest
0 голосов
/ 17 октября 2019

Предположим, у меня есть таблица, в которой записываются изменения в моей базе данных с течением времени:

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). Я хотел бы удалить эти строки. Я нашел много ссылок на то, как использовать общее табличное выражение для удаления повторяющихся строк из таблицы. Таким образом, можно удалить дубликаты (игнорируя столбец TimeOfChange) строк. Но это также удалит (R5), потому что это то же самое, что и R1. Я хочу удалить только те строки, которые имеют те же значения ABC, что и предыдущая строка, если они упорядочены по столбцу TimeOfChange. Как мне это сделать?

edit: Вы можете предположить, что все значения TimeOfChange являются уникальными

Ответы [ 2 ]

1 голос
/ 17 октября 2019

Предполагая, что вы действительно имели в виду «когда тот же A, B, C произошел в самый последний день до того, когда были какие-либо данные», это должно быть полезно для определения строк, которые необходимо удалить:

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 с некоторым косвенным действием, чтобы принудительно создать временную таблицу.

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? Собираетесь ли вы выполнять запрос повторно, пока строки не будут затронуты?

1 голос
/ 17 октября 2019

Предполагая, что 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
);

Итак, сначала вы хотите определить, какие строки являются «следующими», а затем проверить, имеет ли «следующая» те же значения, что и «ток". Для них «следующий» будет формировать набор результатов, который вы хотите использовать в DELETE. select * from data существует, чтобы обойти повторное использование таблицы в DELETE и в подзапросе.

Вы, вероятно, получите гораздо лучшую производительность, если разделите логику в хранимую процедуру и сохраните идентификаторы для строкбыть удаленным во временную таблицу.

См. DB Fiddle

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...