За последние 24 часа нас посетил 22821 программист и 1271 робот. Сейчас ищут 748 программистов ...

Выборка или подсчет дупликатов

Тема в разделе "PHP и базы данных", создана пользователем romach, 10 май 2017.

  1. romach

    romach Старожил

    С нами с:
    26 окт 2013
    Сообщения:
    2.904
    Симпатии:
    719
    Я ничего не имею ввиду, нужна выборка записей, с повторяющимися значениями login. Типа вот так:
    Код (Text):
    1. SELECT
    2.   a.id,
    3.   a.name
    4. FROM TableWithDuplicates a
    5. WHERE EXISTS(SELECT b.id
    6.              FROM TableWithDuplicates b
    7.              WHERE a.name = b.name AND a.id <> b.id);
     
  2. romach

    romach Старожил

    С нами с:
    26 окт 2013
    Сообщения:
    2.904
    Симпатии:
    719
    Да какая разница как он передается? Может он вообще по ssh подгружается другим серваком по крону. Тебе на вход передают имя файла, у него есть расширение. Всё.
     
  3. mkramer

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

    С нами с:
    20 июн 2012
    Сообщения:
    8.555
    Симпатии:
    1.754
    Ну с первой задачей ты сто процентов не справился, там должно было быть что-то вроде (на скорую руку)
    Код (Text):
    1. select  * from Users u where (select count(1) from Users u1 where u1.login=u.login) > 1;
    Второе тебе показал @romach. Тут типично для ООП надо абстрагироваться откуда получен файл, тебя не просили полностью рабочий модуль написать, тебя просили простенькую иерархию сделать.

    Пока я писал свой, @romach свой запрос дал... В общем, не справился с заданием, уж извини...
     
  4. askanim

    askanim Старожил

    С нами с:
    7 апр 2016
    Сообщения:
    2.201
    Симпатии:
    166
    Адрес:
    GABRIEL
    Да про дубликаты я не срастил просто. Запрос не сложный, но я бы его избежал. Лучше обработкой дубликатов нагрузить php машину чем серв бд.
     
  5. mkramer

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

    С нами с:
    20 июн 2012
    Сообщения:
    8.555
    Симпатии:
    1.754
    Понятно, что во втором задании не предполагается, что передаётся $_FILES["xxx"]["tmp_name"], предполагается, что расширение известно.

    БД справится, это раз, зачем мучать php. А два - надо делать то, что попросили в задании. Самодеятельность в работе не приветствуется. Вот у меня есть заказчик, который после каждого успешной отправки всплывающей формы, требует переходить на страничку с текстом "Спасибо большое". Лично я бы не стал выделять отдельную страничку под такую "важную" задачу, вывел бы второе окошко, но его сеошник убеждён в том, что это необходимо, чтоб ставить цели в метрике и аналитиксе. Ну и хрен с ним, делаю, так как просят.
     
    romach нравится это.
  6. romach

    romach Старожил

    С нами с:
    26 окт 2013
    Сообщения:
    2.904
    Симпатии:
    719
    Почему это штука для обработки данных ищет дубликаты хуже штуки для обработки текста?
     
    Fell-x27 и mkramer нравится это.
  7. askanim

    askanim Старожил

    С нами с:
    7 апр 2016
    Сообщения:
    2.201
    Симпатии:
    166
    Адрес:
    GABRIEL
    Нет php обработает данные быстрее. Чем бд. Тут не будут циркулировать запрсоы в цикле для проверки дубликатов. А значи тмодно обойтись без вложенных запросов и join. Я использую join только тогда когда не знаю как не делать так чтобы не делать запросы в цикле.
     
  8. mkramer

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

    С нами с:
    20 июн 2012
    Сообщения:
    8.555
    Симпатии:
    1.754
    С чего вдруг? Чтоб это сделать на php, тебе надо выбрать всю таблицу в память. Нахрена? При индексе по login вложенный запрос в моём случае отработает ун моменто. Сервера БД пишут люди поумнее нас с тобой, и не на php, а на C, который в сотни раз быстрее. У меня вообще правило: вся фильтрация на БД. Нарушил его только один раз пока - когда у меня в условия фильтрации пришлось включить длину маршрутов, рассчитанную Яндекс.картами (к тому же, и у яндекса и у гугла есть условие, запрещающее пользовать это api только на серваке без отображения проложенного маршрута на картах). И то, сначала у меня БД считает расстояние по прямой, отфильтровывает слишком большое (поскольку длина маршрута не может быть меньше длины по прямой никак), и только потом уже результат передаётся яндексу.
     
  9. askanim

    askanim Старожил

    С нами с:
    7 апр 2016
    Сообщения:
    2.201
    Симпатии:
    166
    Адрес:
    GABRIEL
    @mkramer бд при множественных запросах ляжет быстрее чем php. Знаю проходил.
     
  10. romach

    romach Старожил

    С нами с:
    26 окт 2013
    Сообщения:
    2.904
    Симпатии:
    719
    Где тут множественные запросы? Или ты думаешь оно вот так в лоб все херачит, без фильтрации, индексов и кэшей?
     
    mkramer нравится это.
  11. mkramer

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

    С нами с:
    20 июн 2012
    Сообщения:
    8.555
    Симпатии:
    1.754
    Согласен, но не в этом случае. Обычно ложится, если там 6-7 join в одном запросе. И ещё зависит от того, насколько продуманы индексы. Можно сделать так, что и не ляжет.
     
  12. romach

    romach Старожил

    С нами с:
    26 окт 2013
    Сообщения:
    2.904
    Симпатии:
    719
    Да хоть тысячами, если ты про join и вложенные запросы говоришь "кому как нравится", то таки иди учи. Потому что важно лишь то, что нравится планировщику запроса )
     
  13. mkramer

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

    С нами с:
    20 июн 2012
    Сообщения:
    8.555
    Симпатии:
    1.754
    Провёл эксперимент на большой таблице. Таблицу
    Код (Text):
    1.  
    2. CREATE TABLE `doubles` (
    3.     `id` INT(11) NOT NULL AUTO_INCREMENT,
    4.     `login` VARCHAR(50) NOT NULL,
    5.     `some_info` TEXT NOT NULL,
    6.     PRIMARY KEY (`id`),
    7.     INDEX `login` (`login`)
    8. )
    9. COLLATE='utf8_general_ci'
    10. ENGINE=InnoDB;
    заполнил с помощью
    PHP:
    1. <?php
    2. require_once __DIR__ . "/vendor/autoload.php";
    3.  
    4. $db= new mysqli("localhost", "root", "", "test");
    5.  
    6. $faker = Faker\Factory::create();
    7.  
    8. $logins = [];
    9.  
    10. for ($i = 1; $i <= 10000; $i++) {
    11.     do {
    12.         $login = $faker->words(3, true);
    13.     } while (in_array($login, $logins));
    14.     $logins[] = $login;
    15. }
    16.  
    17. $stmt = $db->prepare("insert into doubles set login=?, some_info=?");
    18. for ($i = 1; $i < 500000; $i++) {
    19.     $stmt->bind_param("ss", $login, $some_info);
    20.     $login = $logins[rand(0, 10000)];
    21.     $some_info = $faker->paragraphs(6, true);
    22.     $stmt->execute();
    23. }
    потом попробовал так три запроса:
    PHP:
    1. <?php
    2. $db = new mysqli("localhost", "root", "r124h5rgn", "test");
    3. $t1 = microtime(true);
    4. $res = $db->query("SELECT a.id, a.login FROM doubles a WHERE EXISTS(SELECT b.id FROM doubles b WHERE a.login = b.login AND a.id <> b.id);", MYSQLI_USE_RESULT);
    5. while ($res->fetch_assoc());
    6. echo microtime(true) - $t1, " time A\n";
    7. $res->free_result();
    8. $t1 = microtime(true);
    9. $res = $db->query("SELECT a.id, a.login FROM doubles a WHERE (SELECT count(1) FROM doubles b WHERE a.login = b.login) > 1;", MYSQLI_USE_RESULT);
    10. while ($res->fetch_assoc());
    11. echo microtime(true) - $t1, " time B\n";
    12. $res->free_result();
    13. $t1 = microtime(true);
    14. $res = $db->query("select a.id, a.login from doubles a", MYSQLI_USE_RESULT);
    15. while ($res->fetch_assoc());
    16. echo microtime(true) - $t1, " TIME C\n";
    17. $res->free_result();
    Последний без условия, чтоб было с чем сравнить. Получилось, что запрос от @romach работает в 3 раза быстрее моего, за вполне допустимые 5 секунд, а вот мой запрос плохой, признаю, работает долго, 17 секунд. Запрос без условия - одну секунду. Но тут надо учитывать, что я взял маловато уникальных логинов, ситуация получилась, что у меня все 500 000 дубли, худший случай. Когда генерацию запускал, не подумал.
    --- Добавлено ---
    При этом исполнение запросов в HeideSQL показывает интересную штуку:
    Код (Text):
    1.  
    2. SELECT a.id, a.login FROM doubles a WHERE EXISTS(SELECT b.id FROM doubles b WHERE a.login = b.login AND a.id <> b.id);
    3. /* Затронуто строк: 0  Найденные строки: 499 946  Предупреждения: 0  Длительность  1 запрос: 0,032 sec. (+ 4,531 sec. network) */
    4. SELECT a.id, a.login FROM doubles a WHERE (SELECT count(1) FROM doubles b WHERE a.login = b.login) > 1;
    5. /* Затронуто строк: 0  Найденные строки: 499 946  Предупреждения: 0  Длительность  1 запрос: 0,047 sec. (+ 17,110 sec. network) */
    Честно говоря, я не совсем понимаю, что за время он даёт в скобках.
     
    romach нравится это.
  14. Dmitriy A. Arteshuk

    Dmitriy A. Arteshuk Активный пользователь

    С нами с:
    19 янв 2012
    Сообщения:
    2.445
    Симпатии:
    66
    Адрес:
    Зеленоград
    А так? )
    Код (Text):
    1. SELECT id, login, COUNT(login) as num FROM doubles WHERE true GROUP BY login HAVING num > 1 ORDER BY num DESC
     
  15. mkramer

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

    С нами с:
    20 июн 2012
    Сообщения:
    8.555
    Симпатии:
    1.754
    @Dmitriy A. Arteshuk, так ты не получишь все дубли. Твой запрос в моём случае выдаст 10000 уникальных логинов, и сколько раз каждый использовался (хотя и работает мгновенно). А надо было, чтоб ещё id всех дублей были. Хотя ты навёл меня на мысль, и вот запрос-победитель:
    Код (Text):
    1.  
    2. select group_concat(id separator ','), login,count(login) c from doubles group by login having c > 1;
    3. /* Затронуто строк: 0  Найденные строки: 10 000  Предупреждения: 0  Длительность  1 запрос: 0,016 sec. (+ 0,687 sec. network) */
    Если действительно результат надо разобрать на php, то лучше не придумать, разбить потом это из php на отдельные id элементарно
     
  16. MouseZver

    MouseZver Суперстар

    С нами с:
    1 апр 2013
    Сообщения:
    7.752
    Симпатии:
    1.322
    Адрес:
    Лень
    SELECT * FROM users WHERE login IN
    (SELECT login FROM users
    GROUP BY login HAVING COUNT(login)>1)
     
  17. igordata

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

    С нами с:
    18 мар 2010
    Сообщения:
    32.410
    Симпатии:
    1.768
  18. mkramer

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

    С нами с:
    20 июн 2012
    Сообщения:
    8.555
    Симпатии:
    1.754
    В худшем случае (который я нагенерировал) этот запрос тоже работает медленно. Вернее, долго возвращает результаты, я так понял, что время уходит не на запрос, а на передачу результатов клиенту. Хотя почему MYSQLI_USE_RESULT процесс не ускоряет, я не понял