2013-12-01 5 views
1

Мне пришлось использовать и поддерживать старую схему базы данных для игровых серверов ...
Действительно плохой. Каждый столбец с данными, которые могли иметь нечисловой символ в нем, хранился как текст.
Я преобразовал каждый столбец в соответствующий тип данных, но теперь у меня возникла проблема с настройкой первичного индекса.
Должна быть id, запись, содержащая уникальную идентификационную строку конкретного пользователя. (это варчар).
Из-за предыдущего отсутствия индексации и невинной ошибки, которую невозможно решить из-за наших многочисленных игровых серверов (у нас их было много, и в прошлом их было много), мы имеем несколько повторяющихся строк и, следовательно, не можем установить столбец в качестве основного индекса.Удалить дубликаты строк в моем конкретном случае?

У меня очень мало опыта работы с MySQL или SQL в целом. Я не знаю, как написать запрос для удаления дубликатов.

Одна из наших таблиц состоит из двух столбцов: id и lst (varchar). Для этого дубликаты имеют полностью идентичные строки из-за отсутствия ограничений в запросе обновления.

Другой немного сложнее. Он имеет ту же колонку id, и довольно много. Однако есть три вопроса: id, cur (int) и mdl (varchar). Правило дублирования поиска здесь немного сложнее. Во-первых, в зависимости от того, что имеет значение mdl, отличное от определенного значения (пусть оно будет «default.mdl», например), скорее всего, будет последней информацией. Во-вторых, тот, который имеет наибольшее значение cur, скорее всего, будет правильным.

Исходя из этого, мне нужно сохранить последнюю (скорее всего, правильную) строку в каждой (не обеих) двух таблицах за каждые id.

Как это сделать только с SQL?

Редактировать: Причина, по которой я не делаю этого вручную, состоит в том, что каждая таблица имеет ~ 186 000 строк, и я считаю, что строки 1/20 (~ 9000) являются дубликатами.

+0

Есть ли столбцы, которые являются идентификаторами строк? Обычно называется столбец. «id» будет уникальным, но вы говорите, что для данного идентификатора есть повторяющиеся строки. Если нет, то как различать строки «дублировать» (для ключевых столбцов)? – Bohemian

+0

Я могу взять удар в темноте и утверждать, что строки имеют уникальный идентификатор, указываю ли я один или нет. Должна быть внутренней особенностью MySQL. Я не уверен, может ли этот внутренний уникальный идентификатор использоваться в SQL или нет. – Vercas

ответ

1

Самый простой способ сделать это - это, вероятно, создание временных таблиц, а затем копирование и перемещение некоторых данных.

Немного сложно рассказать вам, что именно делать, поскольку нет схемы для ссылки, но, надеюсь, это поможет вам на правильном пути. Предполагается, что имя таблицы первой упомянутой вами таблицы - my_table_1, второе - my_table_2, что у вас есть разрешение на создание/удаление таблиц, и что вы создали резервную копию своей базы данных (если вы ее не скопировали , остановка сейчас):

# First, add what will become the new id column. We'll rename it shortly. 
ALTER TABLE `my_table_1` 
    ADD `id_new` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST; 

ALTER TABLE `my_table_2` 
    ADD `id_new` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST; 

# Next, build the structure to backup the existing values for future reference. 
CREATE TABLE `temp_table_backup` (
    `id_orig` varchar(255) NOT NULL, 
    `id_new` int(10) NULL DEFAULT NULL, 
    `lst` varchar(255) NULL DEFAULT NULL, 
    `cur` int(10) NULL DEFAULT NULL, 
    `mdl` varchar (255) NULL DEFAULT NULL 
); 

# Now copy the old id values to the backup table 
INSERT INTO temp_table_backup 
    SELECT 
    my_table_1.id, 
    my_table_1.id_new, 
    my_table_1.lst, 
    my_table_2.cur, 
    my_table_2.mdl 
    FROM 
    my_table_1 
    INNER JOIN 
    my_table_2 
    ON 
    my_table_1.id = my_table_2.id GROUP BY my_table_1.id; 

# Create a table to use temporarily. I'm avoiding temporary tables because of the 
# complexity of this whole thing. 
CREATE TABLE `temp_table_1` (
    `id` int(10) NOT NULL 
); 

# Copy values to the new table... 
INSERT INTO temp_table_1 
    SELECT 
    p2.id 
    FROM 
    my_table_1 AS p1, 
    my_table_1 AS p2 
    WHERE 
    p1.lst = p2.lst 
    AND 
    p1.id != p2.id 
    GROUP BY p2.lst; 

# Create another table (temporarily) for my_table_2. This one's kinda tricky, 
# but "ranks" things according to different criteria. 
CREATE TABLE `temp_table_2` (
    `id` int(10) NOT NULL, 
    `id_new` int(10) NULL DEFAULT NULL, 
    `rank` int(10) NULL DEFAULT NULL, 
    `cur` int(10) NULL DEFAULT NULL, 
    `mdl` varchar (255) NULL DEFAULT NULL 
); 

# Copy values to the new table... 
INSERT INTO temp_table_2 
    SELECT t1.id AS id, 
    t1.id_new AS id_new, 
    CASE 
    WHEN t1.mdl = 'default.mdl' AND t1.cur >= t2.cur THEN 4 
    WHEN t1.mdl = 'default.mdl' AND t1.cur < t2.cur THEN 3 
    WHEN t1.mdl != 'default.mdl' AND t1.cur >= t2.cur THEN 2 
    ELSE 1 
    END AS rank, 
    t1.cur AS cur, 
    t1.mdl AS mdl 
    FROM 
    `my_table_2` AS t1, 
    `my_table_2` AS t2 
    WHERE t1.id != t2.id 
    GROUP BY id HAVING MAX(rank) 
    ORDER BY 
    rank DESC, 
    t1.cur DESC, 
    id ASC; 

# Update values in the old table using the values from temp_table_2. 
UPDATE 
    IGNORE `temp_table_2`, 
    `my_table_2` 
SET 
    `my_table_2`.cur = `temp_table_2`.cur, 
    `my_table_2`.mdl = `temp_table_2`.mdl 
WHERE 
    `my_table_2`.id_new = `temp_table_2`.id_new; 

# Delete stale values... 
DELETE 
    FROM my_table_1 
    WHERE id IN (SELECT id FROM temp_table_1); 
# Again... 
DELETE 
    FROM my_table_2 
    WHERE id IN (SELECT id FROM temp_table_1); 

# Next, drop the old id columns and rename id_new to id 
ALTER TABLE 
    `my_table_1` 
DROP `id`; 

ALTER TABLE 
    `my_table_1` 
CHANGE 
    `id_new` `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT; 

ALTER TABLE 
    `my_table_2` 
DROP `id`; 

ALTER TABLE 
    `my_table_2` 
CHANGE `id_new` `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT; 

# Optional. We're done with these tables but you can drop or keep them if you want. 
DROP TABLE IF EXISTS temp_table_1; 
DROP TABLE IF EXISTS temp_table_2; 
DROP TABLE IF EXISTS temp_table_backup; 
+0

Пожалуйста, дайте мне время, чтобы попытаться понять это. Объем SQL в этом сообщении очень запугивает меня из-за моего отсутствия опыта. – Vercas

+1

Я снял этот код и сделал производную работу. Объединил его с некоторыми Lua и зафиксировал его. Благодаря! – Vercas

Смежные вопросы