MySQL - SELECT, убрать дубли, оптимизировать при 2х JOIN

Тема в разделе "Базы данных", создана пользователем Горбушка, 28 мар 2015.

Статус темы:
Закрыта.
Модераторы: latteo
  1. Горбушка

    Горбушка Ищу её...

    Регистр.:
    2 май 2008
    Сообщения:
    3.210
    Симпатии:
    2.238
    Есть запрос:
    Код:
    SELECT 
     `u`.*,
     `c`.`title` AS `city`,
     `p`.`title` AS `phone`
    FROM
     `clients` AS `u`
    LEFT JOIN
     (SELECT `title`, `client_id` FROM `clients_contacts` WHERE `type` = 1) AS `c`
     ON
     `u`.`id` = `c`.`client_id`
    LEFT JOIN
     (SELECT `title`, `client_id` FROM `clients_contacts` WHERE `type` = 3) AS `p`
     ON
     `p`.`client_id` = `u`.`id`;
    Нужно оптимизировать и убрать дубли...

    В настоящий момент если у человека 2 телефона и 2 города (типы 3 и 1 соответственно) - происходит вывод 4 строк, а надо выводить только 2...

    Так же по возможности реализовать с минимальной нагрузкой на БД.

    P.s. этот запрос работает, но выводит слишком много лишнего :) При 100 городах и 100 телефонах у 1 клиента будет 10 000 строчек на 1 человека, а надо 100.

    Код:
    SET FOREIGN_KEY_CHECKS=0;
    
    -- ----------------------------
    -- Table structure for `clients`
    -- ----------------------------
    DROP TABLE IF EXISTS `clients`;
    CREATE TABLE `clients` (
      `id` int(9) NOT NULL AUTO_INCREMENT,
      `title` varchar(255) CHARACTER SET utf8 NOT NULL,
      `type` int(2) NOT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1;
    
    -- ----------------------------
    -- Records of clients
    -- ----------------------------
    INSERT INTO `clients` VALUES ('1', 'Нежность', '1');
    INSERT INTO `clients` VALUES ('2', 'Иванова Инна Ивановна', '2');
    INSERT INTO `clients` VALUES ('3', 'Сидорова Инна Ивановна', '2');
    
    -- ----------------------------
    -- Table structure for `clients_contacts`
    -- ----------------------------
    DROP TABLE IF EXISTS `clients_contacts`;
    CREATE TABLE `clients_contacts` (
      `id` int(9) NOT NULL AUTO_INCREMENT,
      `client_id` int(9) NOT NULL,
      `title` varchar(255) CHARACTER SET utf8 NOT NULL,
      `type` int(2) NOT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=latin1 ROW_FORMAT=COMPACT;
    
    -- ----------------------------
    -- Records of clients_contacts
    -- ----------------------------
    INSERT INTO `clients_contacts` VALUES ('1', '1', '1', '1');
    INSERT INTO `clients_contacts` VALUES ('2', '1', 'Петровка 38', '2');
    INSERT INTO `clients_contacts` VALUES ('3', '2', '1', '1');
    INSERT INTO `clients_contacts` VALUES ('4', '1', '9001234567', '3');
    INSERT INTO `clients_contacts` VALUES ('5', '1', 'admin@test.ru', '4');
    INSERT INTO `clients_contacts` VALUES ('6', '1', '2', '1');
    INSERT INTO `clients_contacts` VALUES ('7', '1', '9001231231', '3');
    INSERT INTO `clients_contacts` VALUES ('8', '2', '9001231231', '3');
    INSERT INTO `clients_contacts` VALUES ('9', '3', '2', '1');
     
    Последнее редактирование: 31 мар 2015
  2. balabolka

    balabolka Писатель

    Регистр.:
    24 мар 2015
    Сообщения:
    5
    Симпатии:
    2
    GROUP BY `phone` в конце не подойдет?
     
  3. Горбушка

    Горбушка Ищу её...

    Регистр.:
    2 май 2008
    Сообщения:
    3.210
    Симпатии:
    2.238
    1 телефон может быть у 2 и более людей. Не прокатит, по идеи...
     
  4. cthulchu

    cthulchu Создатель

    Заблокирован
    Регистр.:
    18 мар 2015
    Сообщения:
    20
    Симпатии:
    24
    хех, ну вот, советы повторяются. мы в чятике уже обсудили проблему:
    один джойн вместо двух с обьединенным через дизьюнкцию условием не пойдет (кстати, можно же туда добавить OR `type` is null),
    group by в конце не пойдет, мы пробовали по айди делать. Я, правда, не помню, почему он не сработал. group by очень логичный выход при дублях.
    есть вариант с дополнительным select'ом вот в этом where:
    ну и в симметричном, конечно. Но это очень неэффективный метод.

    Можно поиграться с where в глобальном селекте.
    Я бы делал через stored procedure, что может быть гораздо эффективней. Единственная альтернатива - переписывание джойнов, по ходу.
    вообще, груп бай надо делать по ключевому, или другому уникальному полю. по айди. айди же у всех один, верно?

    Вообще, имхо, такая хрень случается, когда таблицы проектирует вася, который не понимает, что такое третья нормальная форма. выучили два запроса и ффперед базы таблички клепать.
     
  5. Горбушка

    Горбушка Ищу её...

    Регистр.:
    2 май 2008
    Сообщения:
    3.210
    Симпатии:
    2.238
    Да херня вопрос - таблицы можно переписывать как хочешь )))

    Проектировал их я... Но когда их проектировал, такой задачи не стояло.
     
  6. cthulchu

    cthulchu Создатель

    Заблокирован
    Регистр.:
    18 мар 2015
    Сообщения:
    20
    Симпатии:
    24
    когда проектируешь любые таблицы для любого бизнеса, ты хочешь всюду всегда соблюдать третью нормальную форму. Если не получается, ты делаешь дополнительные таблицы, которые позволяют это делать. В таком случае тебе не пришлось бы делать два одновременных джойна одной и той же таблицы. Мне никогда в жизни такого не приходилось делать. по моему, это явный свидетель проблем с проектированием.

    это как некоторые любители прокладывают локальную сеть для бизнеса на витой паре, выстроив пары так, как им понравилось, а не по стандарту. потом что-то летит, приходит специалист и охреневает от такой витой пары.
     
    latteo нравится это.
  7. balabolka

    balabolka Писатель

    Регистр.:
    24 мар 2015
    Сообщения:
    5
    Симпатии:
    2
    Давай данных тогда побольше, испытывать не на чем, вообще сама структура базы не сильно понятна.
    Кстати, в 90% случаев сложная выборка джойнами работает медленней чем постобработка в скрипте пусть даже с серией дополнительных запросов.
     
  8. cthulchu

    cthulchu Создатель

    Заблокирован
    Регистр.:
    18 мар 2015
    Сообщения:
    20
    Симпатии:
    24
    учитывая, что двиг мускуля гораздо серьезней двига пыхи, если сравнивать их по оптимизированности, я бы сказал наоборот: если ты делаешь в пыхе то, что можно сделать в мускуле, в 95% случаев ты тупишь.

    ЗЫ

    а, вообще, да, горбушка, давай дампы базы.
     
    Горбушка нравится это.
  9. balabolka

    balabolka Писатель

    Регистр.:
    24 мар 2015
    Сообщения:
    5
    Симпатии:
    2
    Да хрен там, для самых примитивных джойнов эта скотина любит создавать временные таблицы (Using temporary; Using filesort), я уже молчу про всякие RAND(), WHERE IN SELECT.... и т.п. Сколько уж раз проверял - красиво в один запрос, далеко не всегда быстро.
     
  10. cthulchu

    cthulchu Создатель

    Заблокирован
    Регистр.:
    18 мар 2015
    Сообщения:
    20
    Симпатии:
    24
    ну так да, джойн - это временная таблица, и что? селект - тоже временная таблица. а что не так с рендомом? В итоге, нагрузку все хотят убрать с проца и передать оперативке, когда можно. всегда. это основа оптимизации кода. Посему, если нам нужна еще одна таблица в памяти ценой процессора - мы в 90% случаев ее хотим там.

    ЗЫ
    у нас сейчас закончится дурацкая дневная порция постов и мы не сможем продолжать обсуждение.
     
Статус темы:
Закрыта.