2013-08-08 4 views
0

Таблица, с которой я имею дело, имеет несколько строк, которые имеют одинаковые значения для lat и lon. Пример показывает, что 1, 3, 5 имеют одинаковое расположение, но атрибут name отличается. hash построен от name, lat и lon и отличается поэтому.Как обновить несколько дубликатов с разными значениями в одной таблице?

BEFORE: 

id | name | lat | lon | flag | hash 
----+------+-----+-----+------+------ 
1 | aaa | 16 | 48 | 0 | 2cd  <-- duplicate 
2 | bbb | 10 | 22 | 0 | 3fc 
3 | ccc | 16 | 48 | 0 | 8ba  <-- duplicate 
4 | ddd | 10 | 23 | 0 | c33 
5 | eee | 16 | 48 | 0 | 751  <-- duplicate 

мне нужно определить «дубликаты» в этой таблице и хочет присвоить флаг 1 (первичная) к одному из них и флаг 2 (вторичного) к другим. Не важно, какой «дубликат» помечен как первичный.

AFTER: 

id | name | lat | lon | flag | hash 
----+------+-----+-----+------+------ 
1 | aaa | 16 | 48 | 1 | 2cd  <-- updated 
2 | bbb | 10 | 22 | 0 | 3fc 
3 | ccc | 16 | 48 | 2 | 8ba  <-- updated 
4 | ddd | 10 | 23 | 0 | c33 
5 | eee | 16 | 48 | 2 | 751  <-- updated 

Я начал экспериментировать с INNER JOINinspired by this post и этот visual description. С этим я могу назначить один и тот же флаг всем дубликатам.

UPDATE table t1 
INNER JOIN table_name t2 
ON 
    t1.lat = t2.lat 
    AND t1.lon = t2.lon 
    AND t1.hash != t2.hash 
SET 
    t1.flag = 2; 

Я также протестировали LEFT OUTER JOIN с WHERE t2.id IS NULL, которые могли бы работать, когда есть только две строки. Тем не менее, я не могу думать, как JOIN должен работать с более двух дубликатов. Марк Харрисон также предполагает «что вы присоединяетесь к столбцам без дубликатов»at the beginning of his post, которые звучат так, как будто это не очень хорошая идея.

Я использую MySQL, если это представляет интерес.

ответ

2

Не уверен, что это очень эффективно, но это works in just one query:

UPDATE t 
JOIN (
    SELECT MAX(t.id) AS maxid, lat, lon 
    FROM t 
    JOIN t AS duplicates 
    USING (lat, lon) 
    GROUP BY lat, lon 
    HAVING COUNT(*) > 1 
) AS maxima USING (lat, lon) 
SET flag = IF(id = maxid, 1, 2); 
+0

Работает очень быстро! Спасибо. Хотя, * SQL Fiddle * не загружается для меня (ни [пример] (http://sqlfiddle.com/#viewSample)). – JJD

0

Предполагая, что вы хотите, чтобы все канонические записи, чтобы иметь flag=1 (а не только те записи, которые имеют дубликаты), вы можете сделать:

UPDATE table t1 
SET t1.flag = 1 
WHERE t1.id in (
    SELECT min(id) 
    FROM table t2 
    WHERE t1.lat = t2.lat 
    AND t1.lon = t2.lon 
); 

UPDATE table 
SET flag = 2 
WHERE flag is null; 

COMMIT; 
+0

Только дубликаты должны быть обновлены. Вывешенное сообщение выдает «Mysql2 :: Ошибка: вы не можете указать целевую таблицу« t1 »для обновления в разделе FROM. – JJD

0

Вы можете сделать это в два этапа:

update table t1 set flag = 2 where id in 
    (select a.id 
    from 
     t1 a 
     join t1 b on (b.lon = a.lon and b.lat = a.lat)); 

update table t1 set flag = 1 where id in 
    (select a.id 
    from t1 a 
    where a.flag = 2 and a.id = 
     (select min(b.id) from t1 b where b.lon = a.lon and b.lat = a.lat) 
    ); 

Это должно использоваться как вдохновение и не как Евангелие (это, вероятно, неправильно) :)

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