Как оптимизировать запрос для удаления дубликатов MySQL?

#mysql #sql #google-cloud-platform

#mysql #sql #google-облачная платформа

Вопрос:

У меня есть таблица из 433 333 записей в базе данных MySQL в Google Cloud, которая выглядит примерно так:

 Album_ID | Track_Len | Track_Name                            | Ft_LName1 | Ft_FName1 | Ft_LName2 | Ft_FName2 | Ft_LName3 | Ft_FName3 | Row_Num |
 --------- ----------- --------------------------------------- ----------- ----------- ----------- ----------- ----------- ----------- --------- 
| N40781  |      5.19 | Tumbala (Da Lata Remix)               | NULL      | Novalima  | NULL      | NULL      | NULL      | NULL      |       1 |
| N40781  |      5.01 | Ruperta (Zeb Remix)                   | NULL      | Novalima  | NULL      | NULL      | NULL      | NULL      |       2 |
| N40781  |      6.35 | Coba Guarango (Toni Economides Remix) | NULL      | Novalima  | NULL      | NULL      | NULL      | NULL      |       3 |
| B15033  |      6.02 | II-V-P                                | Quartet   | ARC       | NULL      | NULL      | NULL      | NULL      |       4 |
| N32395  |      4.47 | My Babe                               | Stigers   | Curtis    | NULL      | NULL      | NULL      | NULL      |       5 |
| N32395  |      5.13 | Thats All Right                       | Stigers   | Curtis    | NULL      | NULL      | NULL      | NULL      |       6 |
  

Обратите внимание, что первичный ключ должен быть (Album_ID,Track_Name).
Дубликатов много, поэтому я запускаю следующее, чтобы попытаться их устранить:

 delete from Track where (Album_ID, Track_Name, Row_Num) IN(
  select Album_ID, Track_Name, MAX(Row_Num)
  from (select Album_ID,Track_Name,Row_Num from Track) as x
  where (Album_ID, Track_Name) IN(
                  select Album_ID,Track_Name
                  from (select Album_ID,Track_Name from Track) as y
                  group by Album_ID, Track_Name
                  having count(*) > 1
                )
  group by Album_ID,Track_Name);
  

Но это занимает слишком много времени и не удаляет все дубликаты сразу.
Есть предложения по оптимизации этого запроса??

  ------------ --------------------- ------ ----- --------- ---------------- 
| Field      | Type                | Null | Key | Default | Extra          |
 ------------ --------------------- ------ ----- --------- ---------------- 
| Album_ID   | varchar(6)          | YES  |     | NULL    |                |
| Track_Len  | decimal(4,2)        | YES  |     | NULL    |                |
| Track_Name | varchar(100)        | YES  |     | NULL    |                |
| Ft_LName1  | varchar(40)         | YES  |     | NULL    |                |
| Ft_FName1  | varchar(40)         | YES  |     | NULL    |                |
| Ft_LName2  | varchar(40)         | YES  |     | NULL    |                |
| Ft_FName2  | varchar(40)         | YES  |     | NULL    |                |
| Ft_LName3  | varchar(40)         | YES  |     | NULL    |                |
| Ft_FName3  | varchar(40)         | YES  |     | NULL    |                |
| Row_Num    | bigint(20) unsigned | NO   | PRI | NULL    | auto_increment |
 ------------ --------------------- ------ ----- --------- ---------------- 
  

Комментарии:

1. Пожалуйста, покажите нам полную схему для Track и ее индексы. И, пожалуйста, покажите нам explain выходные данные для этого запроса.

2. Поскольку Track_Name это может измениться, я бы предложил сделать первичный ключ простым автоматически увеличиваемым целым числом и вместо этого использовать уникальный индекс для обеспечения уникальности альбома / трека. Если вам нужно изменить название трека, ссылка не будет нарушена.

3. Договорились, что уникальный индекс поможет. Но я не могу добавить его, пока в таблице существуют повторяющиеся записи.

Ответ №1:

Традиционный способ сделать это в MySQL использует JOIN и GROUP BY :

 delete t
    from Track t left join
         (select tt.Album_ID, tt.Track_Name, min(tt.row_num) as min_row_num
          from Track tt
          group by tt.Album_ID, tt.Track_Name
         ) tt
         on t.row_number = tt.min_row_num
    where tt.min_row_num is null;
  

При этом используется тот факт, что id является уникальным в целом и, вероятно, первичным ключом. Вы также можете указать это как:

 delete t
    from Track t join
         (select tt.Album_ID, tt.Track_Name, min(tt.row_num) as min_row_num
          from Track tt
          group by tt.Album_ID, tt.Track_Name
         ) tt
         on tt.Album_ID = t.Album_ID and
            tt.Track_Name = t.Track_Name and
            t.row_number > tt.min_row_num;
  

Ответ №2:

Поскольку Album_ID и Track_Name различают каждую строку от другой, попробуйте это с помощью self join:

 delete t from Track t
inner join Track tt
where 
  tt.Album_ID = t.Album_ID and
  tt.Track_Name = t.Track_Name and
  tt.Row_Num < t.Row_Num
  

Комментарии:

1. ОШИБКА 1093 (HY000): Вы не можете указать целевую таблицу ‘t’ для обновления в предложении FROM.

2. Нужен ли мне псевдоним в подзапросе?

3. Нет, это просто Mysql, который не позволяет указать целевую таблицу для обновления. Если у меня будет выход, я вернусь.

4. Что произошло??