Вопрос по many-to-many и join

Белый Ум
Дата: 21.01.2013 13:28:34
Добрый день!
Вопрос такой - можно как-то одним запросом вытянуть лист объектов, в том числе для каждого объекта получить лист связанных с ним many-to-many объектов?

Мой обычный inner join возвращает дубли(как и следовало ожидать) а хотелось бы получить что-то такое(в нотации псевдоязыка):
[[id = 1, name = "имя1", lang = [[id = 1, name = "cpp"], [id = 2, name = "php"] ]], [id = 2, name = "имя2", lang = [[id = 3, name = "python"], [id = 2, name = "php"] ]]]

После весь массив данных передаю шаблону для визуализации.

Прочитал правила форума, постарался их не нарушить в своём первом посте. Спасибо за ответы.

CREATE TABLE IF NOT EXISTS `lang` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(128) COLLATE utf8_unicode_ci NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=6 ;


CREATE TABLE IF NOT EXISTS `programs` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(200) COLLATE utf8_unicode_ci DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=3 ;


CREATE TABLE IF NOT EXISTS `programs_lang` (
  `id_lang` int(11) NOT NULL,
  `id_programs` int(11) NOT NULL,
  `priority` int(11) NOT NULL DEFAULT '0'
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;


INSERT INTO `programs` (`id`, `name`) VALUES
(1, 'парсер'),
(2, 'snippet_site');

INSERT INTO `lang` (`id`, `name`) VALUES
(1, 'python'),
(2, 'php'),
(3, 'cpp'),
(4, 'js'),
(5, 'jango');

INSERT INTO `programs_lang` (`id_lang`, `id_programs`, `priority`) VALUES
(1, 1, 1),
(2, 1, 2),
(3, 1, 3),
(4, 2, 1),
(5, 2, 2);

select programs.id, programs.name, lang.id, lang.name from programs 
INNER JOIN programs_lang ON programs_lang.id_programs = programs.id
INNER JOIN lang ON programs_lang.id_lang = lang.id
tanglir
Дата: 21.01.2013 13:32:43
Белый Ум, нет. Получайте "дубли" и приводите данные к нужному виду на клиенте.
ЗЫ. Хотя, может быть (может быть), стоит посмотреть на group_concat.
Arhat109
Дата: 21.01.2013 15:02:02
Белый Ум,

посмотрите на прилады к Мускулю в части возврата json объектов.

Себе делал класс в PHP, который динамически формирует запрос через GROUP_CONCAT(), возвращая такой json объект. Ничего сложного там нет, но оптимизировать запрос с километровым GROUP_CONCAT() - несколько неуютно.

Отказался, по причине того, что код/структуруБД завсегда правильнее упростить/изменить, чтобы таких задач не возникало "как класса".
ИМХО: дурь это всё, не ведитесь.
Белый Ум
Дата: 21.01.2013 15:33:44
Arhat109, tanglir получается два варианта действий:
1 - group_concat
2 - пост-обработка.
3 N запросов для листа из N объектов

1 вариант не хочется делать, потому что
  • сложно,
  • усложняет архитектуру обработки результатов запроса
  • на самом деле каждый запрос уже содержит в себе по 5 inner jion, если еще сделать group_concat - я совсем перестану что происходит с запросом и его производительностью(!!!!)

    2 вариант, плох тем что:
  • усложняет архитектуру обработки результатов запроса
  • для получения листа из 100 объектов может получиться лист из 100*7*3 дублей. Какбы тянуть в память столько записей, каждая из которых по 10-15 тяжелых полей....

    Данная ситуация возникла при попытке вывести лейблы языков на листе программ. http://www.study-overseas.ru/programs/country/germany/
    На этот раз я переделал архитектуру и сделал вместо many-to-many one-to-many.

    Но как красиво решать эту арх задачу, по прежнему остается не понятно.
  • Arhat109
    Дата: 21.01.2013 19:08:00
    Белый Ум,

    1 вариант - сложно?!? и что там "сложного"? Мне удалось написать класс на ПХП за час - полтора (примерно)... он просто содержит набор методов-примитивов по формированию этого самого group_concat() в соответствии с типами свойств ваших сложных "объектов"...

    2. "пост-обработка" - ?!? наверное да, но надо просто смотреть что и главное зачем, вам надо как-бы объектом... часто удается слегка изменить логику обработки / структуры хранения (а соответственно и запросы) ... и ваши "объекты" уже и не нужны становятся... вполне достаточен простой линейный массив или несколько... и далеко "не факт" что последовательная простая выборка будет идти дольше обощенной...

    ... да и опасения, что дублирование отдаваемых данных (денормализация вывода) - это типа всегда "плохо"... далеко не так. Просто всегда надо отдавать себе отчет что делаешь и зачем...

    3. Я Вам порекомендовал пошукать готовые решения в виде плагинов, отдающих json-объект ... они есть (я тока ссыли не помню)...
    DBConstructor
    Дата: 22.01.2013 03:12:19
    Белый Ум, не вижу в структуре данных связей "много-ко-многим". Все связи таблицы programs_lang с другими только "много-к-одному". Для убирания дублей из результата, просто добавьте модификатор DISTINCT к оператору SELECT.

    SELECT DISTINCT p.id, p.name, l.id. l.name
      FROM programs_lang pl
        INNER JOIN programs p ON p.id = pl.id_programs
        INNER JOIN lang l ON l.id = pl.id_lang
    


    Да и вообще, нучитесь читать мануал! Как говориться - RTFM (http://dev.mysql.com/doc/)
    javajdbc
    Дата: 22.01.2013 07:14:21
    DBConstructor
    Белый Ум, не вижу в структуре данных связей "много-ко-многим". Все связи таблицы programs_lang с другими только "много-к-одному". Для убирания дублей из результата, просто добавьте модификатор DISTINCT к оператору SELECT.

    SELECT DISTINCT p.id, p.name, l.id. l.name
      FROM programs_lang pl
        INNER JOIN programs p ON p.id = pl.id_programs
        INNER JOIN lang l ON l.id = pl.id_lang
    


    Да и вообще, нучитесь читать мануал! Как говориться - RTFM (http://dev.mysql.com/doc/)


    DBConstructor, да не нервничайте так :-)
    многие не знают как делается много-ко-много :-)