Как безопасно удалить только дублированные строки? - PullRequest
0 голосов
/ 20 ноября 2018

У меня есть таблицы, которые содержат 6.820.483, и между этими строками есть много дубликатов, я обнаружил, что при выполнении этого запроса:

SELECT player_id, match_id, team_id, count(*) 
FROM fixtures
GROUP BY player_id, match_id, team_id
HAVING COUNT(*) > 1

пример структуры:

player_id | match_id  | team_id
  19014       2506172    12573
  19014       2506172    12573
  19015       2506172    12573
  19016       2506172    12573
  19016       2506172    12573
  19016       2506172    12573

как я могу безопасно удалить только дубликаты?В приведенном выше примере таблица должна выглядеть следующим образом:

player_id | match_id  | team_id
  19014       2506172    12573
  19015       2506172    12573
  19016       2506172    12573

структура таблицы:

CREATE TABLE IF NOT EXISTS `swp`.`fixtures` (
  `player_id` INT NOT NULL,
  `match_id` INT NOT NULL,
  `team_id` INT NOT NULL,
  INDEX `player_id_idx` (`player_id` ASC),
  INDEX `match_id_idx` (`match_id` ASC),
  INDEX `FK_team_fixtures_id_idx` (`team_id` ASC),
  CONSTRAINT `FK_player_fixtures_id`
    FOREIGN KEY (`player_id`)
    REFERENCES `swp`.`player` (`id`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION,
  CONSTRAINT `FK_match_fixtures_id`
    FOREIGN KEY (`match_id`)
    REFERENCES `swp`.`match` (`id`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION,
  CONSTRAINT `FK_team_fixtures_id`
    FOREIGN KEY (`team_id`)
    REFERENCES `swp`.`team` (`id`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;

Ответы [ 3 ]

0 голосов
/ 20 ноября 2018

Нет другого решения, кроме резервного копирования отдельных строк таблицы во временную таблицу и последующего ее восстановления, как это предложил @Robert Kock, но:дубликаты могут появиться снова, как и раньше.Поэтому перед восстановлением таблицы выполните следующую инструкцию:

ALTER TABLE swp.fixtures ADD PRIMARY KEY(player_id, match_id, team_id);

, чтобы добавить первичный ключ из нескольких столбцов, чтобы проблема больше не возникала. Edit1 От: https://dev.mysql.com/doc/refman/8.0/en/ansi-diff-select-into-table.html

MySQL Server не поддерживает SELECT ... INTO TABLE Расширение Sybase SQL.Вместо этого MySQL Server поддерживает стандартный синтаксис SQL INSERT INTO ... SELECT, что, в сущности, одно и то же.См. Раздел 13.2.6.1, «ВСТАВИТЬ ... ВЫБРАТЬ Синтаксис».Например:

INSERT INTO tbl_temp2 (fld_id)
    SELECT tbl_temp1.fld_order_id
    FROM tbl_temp1 WHERE tbl_temp1.fld_order_id > 100;

Edit2 (после предложения Гордона Линоффа)Таким образом, весь код должен быть:

CREATE TABLE tmp_fixtures AS 
    SELECT DISTINCT player_id, match_id, team_id FROM fixtures;

TRUNCATE TABLE fixtures;

ALTER TABLE fixtures ADD PRIMARY KEY(player_id, match_id, team_id);

INSERT INTO fixtures (player_id, match_id, team_id)
    SELECT player_id, match_id, team_id FROM tmp_fixtures;

DROP TABLE tmp_fixtures;

Используйте с осторожностью и только если у вас есть резервная копия ваших данных.

0 голосов
/ 20 ноября 2018

Роберт и Форпас оба дали гораздо лучшие ответы, но технически я считаю, что это можно сделать без создания новой таблицы (по крайней мере, в MSSQL).Я пытался перевести на MySQL.Опять же, я, вероятно, никогда бы не сделал это таким образом, особенно на больших наборах данных, но это было интересное упражнение.

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

DECLARE @i INT = 0

WHILE @i < 6820483
BEGIN
  DELETE FROM f
  FROM (
      SELECT *
      FROM fixtures
      WHERE player_id IN (SELECT player_id FROM fixtures GROUP BY player_id HAVING COUNT(*) > 1)
      LIMIT 1
  ) f

  SET @i = @i + 1
END

Также, как указано в других ответах, вы, вероятно, захотите создать Primary Key, чтобы предотвратить это в будущем.

0 голосов
/ 20 ноября 2018

Я не ожидаю MySQL, но вы можете попробовать это (если вы уверены, что новые записи не будут вставлены за это время):

CREATE TABLE tmp_fixtures
(
  player_id INT NOT NULL,
  match_id  INT NOT NULL,
  team_id   INT NOT NULL
);

SELECT DISTINCT
       player_id,
       match_id,
       team_id
  INTO tmp_fixtures
  FROM fixtures;

TRUNCATE TABLE fixtures;

Чтобы убедиться, что дублирующиеся записи не будутСозданный больше, вы можете сделать следующее:

ALTER TABLE fixtures ADD PRIMARY KEY (player_id, match_id, team_id);

После этого заново заполнить таблицу и очистить:

INSERT INTO fixtures (player_id, match_id, team_id)
  SELECT player_id,
         match_id,
         team_id
  FROM   tmp_fixtures;

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