За последние 24 часа нас посетили 16647 программистов и 1681 робот. Сейчас ищут 819 программистов ...

Удалить дубликаты, объединить строки

Тема в разделе "MySQL", создана пользователем nikemat, 4 апр 2015.

  1. nikemat

    nikemat Guest

    Здравствуйте!
    Использую следующий запрос для удаления дубликатов из таблицы (он работает):
    Код (Text):
    1.  
    2. DELETE FROM `table`
    3. WHERE id IN (
    4. SELECT * FROM (
    5. SELECT id
    6. FROM `table`
    7. GROUP BY CONCAT(phone)
    8. HAVING count(*) > 1
    9. ) AS a
    10. )
    можно как-то сделать, чтобы при удалении дубликатов данные из другой колонки в удаленной строке записывались (присоединялись) в другой столбец?
    например:
    и т.д., в первых двух номера совпадают, значит один из них будет удалён (кстати- кто?)
    и мне надо, чтобы в итоге после удаления было так:
    собственно, как получить дубликаты я знаю. а как сделать конкатенацию?
    в теории: переписать дублирующие строки с UPDATE (поставить phone на NULL у дубликатов и перенести имя) потом стереть все записи где phone = NULL
    а в коде не могу сформулировать(
     
  2. artoodetoo

    artoodetoo Суперстар
    Команда форума Модератор

    С нами с:
    11 июн 2010
    Сообщения:
    11.131
    Симпатии:
    1.251
    Адрес:
    там-сям
    сделать конкатенацию при группировке - group_concat()

    в твоем примере запрос сделан не по правилам - в списке полей select должны быть только поля из group by или агрегатные функции от других полей. поэтому какой именно id будет выведен невозможно сказать. "not given", как говорят буржуи.
    можешь писать, например, min(id) тогда будет определенность.
     
  3. nikemat

    nikemat Guest

    а как переписать по правилам?)
     
  4. artoodetoo

    artoodetoo Суперстар
    Команда форума Модератор

    С нами с:
    11 июн 2010
    Сообщения:
    11.131
    Симпатии:
    1.251
    Адрес:
    там-сям
     
  5. retvizan

    retvizan Активный пользователь

    С нами с:
    27 дек 2013
    Сообщения:
    68
    Симпатии:
    22
    Код (Text):
    1. update `таблица` t left join
    2. (SELECT max(id) id, phone, group_concat(fio) fio FROM `таблица` group by 2) t1  using(id)
    3. set t.phone=t1.phone, t.fio=t1.fio;
    Среди дубликатов у строки с максимальным id будут сгруппированы fio, у прочих дублей fio и phone будут null (в принципе достаточно выставить у одной колонки).

    О том как правильно делать группировку см статью Группировка в MySQL

    Что касается исходного запроса delete, то дубли можно удалять без вложенного подзапроса
    Код (Text):
    1. DELETE t1 FROM t t1 LEFT JOIN t t2
    2. ON t1.col = t2.col AND t1.id < t2.id
    3. WHERE t2.id IS NOT NULL;
     
  6. nikemat

    nikemat Guest

    этот запрос работает, спасибо!

    Не получалось в одном, убил целый день на поиск работающего метода удаления дубликатов.
    оказывается, mysql старый- 5.5. обновлять долго. а работает только описанный способ, с вложенностью.
    просто варианты основаны либо на создании временных таблиц, либо на указании столба с уникальным индексом. у меня ни то ни другое не работает без ошибок.
    я ещё с альясами ещё не разобрался. если несложно. адаптируйте названия полей в вашем примере к моей задаче, попробую выполнить. вдруг заработает) таблица- `tbl`, колонки: id, phone, fio
     
  7. retvizan

    retvizan Активный пользователь

    С нами с:
    27 дек 2013
    Сообщения:
    68
    Симпатии:
    22
    Ещё как получается, указанный запрос удаляет из таблицы t (у вас tbl) дубликаты (строки с одинаковыми значениями поля col (у вас phone)) с меньшим id (у вас тоже id).

    Обновлять не надо, в новых версиях тоже самое ограничение.
     
  8. nikemat

    nikemat Guest

    ха.. действительно работает. ну где-то что-то пропустил наверное. спасибо!

    и ещё вопрос по объединению строк. допустим, исходное содержание таблицы такое:

    Код (Text):
    1.  
    2. id  phone   fio     address
    3. 1   898989  Иванов    Москва
    4. 2   898989  Иванов    СПБ
    5. 3   787878  Петров    СПБ
    6. 4   989898  Сидоров  НН
    7. 5   989898  Кузнецов    Москва
    выполняем запрос конкатенации, получили:
    Код (Text):
    1.  
    2. id  phone   fio                 address
    3. 1   898989  Иванов,Иванов       Москва
    4. 2   787878  Петров                СПБ
    5. 3   989898  Сидоров,Кузнецов НН
    6.  
    здесь удалены дубликаты столбца phone у г-на Иванова, в fio прописалась его фамилия.
    но! она не должна повторять то, что уже есть в поле. только если там другое значение, например- Кузнецов.
    и ещё есть один столбец- address, там в процессе объединения тоже должна производиться конкатенация по такому же принципу.
    в общем, мне нужно получить вот такой результат:
    Код (Text):
    1.  
    2. id  phone   fio                     address
    3. 1   898989  Иванов                Москва, СПБ
    4. 2   787878  Петров                СПБ
    5. 3   989898  Сидоров,Кузнецов НН, Москва
    6.  
     
  9. retvizan

    retvizan Активный пользователь

    С нами с:
    27 дек 2013
    Сообщения:
    68
    Симпатии:
    22
    group_concat(distinct `имя колонки`)
     
  10. nikemat

    nikemat Guest

    Код (Text):
    1. update tbl t left join
    2. (SELECT max(id) id, phone, group_concat(distinct `fio`) fio, group_concat(distinct `address`) address FROM tbl group by 2) t1  using(id)
    3. set t.phone=t1.phone, t.fio=t1.fio,t.address=t1.address;
    вроде так работает) только сделал мне несколько пустых строк((
     
  11. retvizan

    retvizan Активный пользователь

    С нами с:
    27 дек 2013
    Сообщения:
    68
    Симпатии:
    22
    В смысле пустых? У дубликатов с меньшим id поля phone, fio и address будут null.
    В принципе null достаточно выставлять одному полю, всё равно их потом удалять.
     
  12. nikemat

    nikemat Guest

    а, ну если потом удалять, то норм. Здесь null присваивается? t.fio=t1.fio,t.address=t1.address
    у меня удаление отдельной командой, а слияние контактов- отдельной.
     
  13. nikemat

    nikemat Guest

    а ещё маленький вопросик, тоже вроде простой, но что-то не нашёл сам решения.
    в общем, есть ещё один столбец, называется status. мне нужно, чтобы слияние и удаление действовали только для status=0, а всё остальное- без изменений.
    полагаю, что где-то в конце запроса нужно вставить WHERE status=0, но пока у меня не сработало. подскажите, где поставить условие.
     
  14. retvizan

    retvizan Активный пользователь

    С нами с:
    27 дек 2013
    Сообщения:
    68
    Симпатии:
    22
    И в конце запроса и в подзапросе.
     
  15. nikemat

    nikemat Guest

    так вот и не могу понять, куда вставлять-то это условие((

    Код (Text):
    1. update tbl t left join
    2. (SELECT max(id) id, phone, group_concat(distinct `fio`) fio, group_concat(distinct `address`) address FROM tbl group by 2 WHERE `stastus`=0) t1  using(id)
    3. set t.phone=t1.phone, t.fio=t1.fio,t.address=t1.address WHERE `stastus`=0;
    4.  
    5.  
    6. DELETE t1 FROM tbl t1 LEFT JOIN tbl t2
    7. ON t1.phone = t2.phone AND t1.id < t2.id
    8. WHERE (`status`=0 and t2.id IS NOT NULL);
     
  16. retvizan

    retvizan Активный пользователь

    С нами с:
    27 дек 2013
    Сообщения:
    68
    Симпатии:
    22
    в подзапросе where и group by местами поменяйте.
     
  17. artoodetoo

    artoodetoo Суперстар
    Команда форума Модератор

    С нами с:
    11 июн 2010
    Сообщения:
    11.131
    Симпатии:
    1.251
    Адрес:
    там-сям
    правильная последовательность фраз в одном запросе (что-то может отсутствовать, менять местами нельзя):
    Код (Text):
    1. SELECT
    2. FROM
    3. WHERE
    4. GROUP BY
    5. HAVING
    6. ORDER BY
    7. LIMIT