Как найти дубликаты в 2 столбцах, а не в 1 - PullRequest
90 голосов
/ 13 марта 2009

У меня есть таблица базы данных MySQL с двумя интересующими меня столбцами. Каждый из них может иметь дубликаты, но у них никогда не должно быть дубликатов ОБА, имеющих одинаковое значение.

stone_id может иметь дубликаты, если для каждого upsharge заголовок отличается, и наоборот. Но скажем, например, stone_id = 412 и upcharge_title = "сапфир", что комбинация должна встречаться только один раз.

Это нормально:

stone_id = 412 upcharge_title = "sapphire"
stone_id = 412 upcharge_title = "ruby"

Это НЕ нормально:

stone_id = 412 upcharge_title = "sapphire"
stone_id = 412 upcharge_title = "sapphire"

Есть ли запрос, который найдет дубликаты в обоих полях? И, если возможно, есть ли способ настроить мою базу данных так, чтобы это не разрешалось?

Я использую MySQL версии 4.1.22

Ответы [ 6 ]

168 голосов
/ 13 марта 2009

Вы должны установить составной ключ между двумя полями. Это потребует уникального stone_id и upcharge_title для каждой строки.

Чтобы найти существующие дубликаты, попробуйте следующее:

select   stone_id,
         upcharge_title,
         count(*)
from     your_table
group by stone_id,
         upcharge_title
having   count(*) > 1
32 голосов
/ 15 июня 2012

Мне показалось полезным добавить индекс unqiue, используя «ALTER IGNORE», который удаляет дубликаты и вводит уникальные записи, что звучит так, как вы хотели бы. Таким образом, синтаксис будет:

ALTER IGNORE TABLE `table` ADD UNIQUE INDEX(`id`, `another_id`, `one_more_id`);

Это фактически добавляет уникальное ограничение, означающее, что у вас никогда не будет дубликатов записей, а IGNORE удаляет существующие дубликаты.

Подробнее об eh ALTER IGNORE можно прочитать здесь: http://mediakey.dk/~cc/mysql-remove-duplicate-entries/

Обновление: @Inquisitive сообщил мне, что это может не работать в версиях MySql> 5.5:

Сбой в MySQL> 5.5 и в таблице InnoDB, а также в Percona из-за их функция быстрого создания индекса InnoDB [http://bugs.mysql.com/bug.php?id=40344]. В этом случае сначала запустите set session old_alter_table=1, а затем приведенную выше команду будет работать нормально

Обновление - ALTER IGNORE Удалено в 5.7

Из документов

Начиная с MySQL 5.6.17, предложение IGNORE устарело и его использование генерирует предупреждение. IGNORE удален в MySQL 5.7.

Один из разработчиков MySQL дает две альтернативы :

  • Группировка по уникальным полям и удаление, как показано выше
  • Создайте новую таблицу, добавьте уникальный индекс, используйте INSERT IGNORE, например:
CREATE TABLE duplicate_row_table LIKE regular_row_table;
ALTER TABLE duplicate_row_table ADD UNIQUE INDEX (id, another_id);
INSERT IGNORE INTO duplicate_row_table SELECT * FROM regular_row_table;
DROP TABLE regular_row_table;
RENAME TABLE duplicate_row_table TO regular_row_table;

Но, в зависимости от размера вашего стола, это может быть нецелесообразно

6 голосов
/ 13 марта 2009

Вы можете найти дубликаты, как это ..

Select
    stone_id, upcharge_title, count(*)
from 
    particulartable
group by 
    stone_id, upcharge_title
having 
    count(*) > 1
4 голосов
/ 13 марта 2009

Чтобы найти дубликаты:

select stone_id, upcharge_title from tablename group by stone_id, upcharge_title having count(*)>1

Чтобы избежать этого в будущем, создайте составной уникальный ключ для этих двух полей.

3 голосов
/ 13 марта 2009

Между прочим, составное уникальное ограничение на таблицу могло бы предотвратить это в первую очередь.

ALTER TABLE table
    ADD UNIQUE(stone_id, charge_title)

(Это действительный T-SQL. Не уверен насчет MySQL.)

0 голосов
/ 20 августа 2010

этот пост помог мне, но я тоже хотел знать, как удалить и сохранить одну из строк ... вот PHP-решение для удаления дублирующих строк и сохранения одной (в моем случае было только 2 столбца и в функции для очистки повторяющихся ассоциаций категорий)

$dupes = $db->query('select *, count(*) as NUM_DUPES from PRODUCT_CATEGORY_PRODUCT group by fkPRODUCT_CATEGORY_ID, fkPRODUCT_ID having count(*) > 1');
if (!is_array($dupes))
    return true;
foreach ($dupes as $dupe) {
    $db->query('delete from PRODUCT_CATEGORY_PRODUCT where fkPRODUCT_ID = ' . $dupe['fkPRODUCT_ID'] . ' and fkPRODUCT_CATEGORY_ID = ' . $dupe['fkPRODUCT_CATEGORY_ID'] . ' limit ' . ($dupe['NUM_DUPES'] - 1);
}

(предел NUM_DUPES - 1) - это то, что сохраняет единственную строку ...

спасибо всем

...