Использование Joins, Group By и подзапросов, Oh My! - PullRequest
3 голосов
/ 27 июля 2011

У меня есть база данных с таблицей для сведений о пони, другая для сведений о контактах (владельцы и заводчики), а затем несколько других небольших таблиц для параметров (цвета, округа, коды городов и т. Д.). Чтобы дать мне список существующих профилей пони с указанием их различных деталей, я использую следующий запрос:

SELECT *
    FROM profiles
        INNER JOIN prm_breedgender
            ON profiles.ProfileGenderID = prm_breedgender.BreedGenderID
        LEFT JOIN contacts
            ON profiles.ProfileOwnerID = contacts.ContactID
        INNER JOIN prm_breedcolour
            ON profiles.ProfileAdultColourID = prm_breedcolour.BreedColourID
    ORDER BY profiles.ProfileYearOfBirth ASC $limit

В приведенном выше примере таблица «анкеты» - это моя основная таблица (содержащая информацию о пони), «контакты» занимают второе место по важности, как и информация о владельце и заводчике. Меньшие таблицы параметров могут быть идентифицированы по их префиксу prm_. Приведенный выше запрос работает нормально, но я хочу сделать больше.

Первая большая проблема заключается в том, что я хочу группировать результаты по полу: жеребцы, кобылы, мерины ... Я использовал << GROUP BY prm_breedgender.BreedGender >> или << GROUP BY ProfileBreedGenderID >> перед моей строкой ORDER BY , но затем возвращает только два результата из всех моих доступных профилей. Я прочитал об этом, и, очевидно, мне нужно реорганизовать мой запрос, чтобы включить GROUP в мое основное предложение SELECT. Как это сделать, однако, меня смущает verrrrrrry. Пошаговая помощь здесь будет потрясающей.

Как еще одно замечание по поводу вышеизложенного - вы, возможно, заметили переменную $ limit в конце моего запроса. Это для нумерации страниц, особенность, которую я хочу сохранить. Однако я не должен думать, что это проблема.

Моя вторичная проблема - скорее организационная. Вы можете увидеть, где я вытащил информацию о своем владельце из таблицы контактов здесь:

    LEFT JOIN contacts
        ON profiles.ProfileOwnerID = contacts.ContactID

Я мог бы добавить еще одно условие:

        AND profiles.ProfileBreederID = contacts.ContactID

с намерением указать владельца и заводчика пони, где доступна информация о любом из них. Я не уверен, как отобразить эту информацию, так как $ row ['ContactName'] может применяться в качестве владельца или заводчика.

Это случай простого запуска двух запросов, а не одного? Назначить переменную $ foo для первого запуска запроса, а затем просто выполнить еще один отдельный запрос и присвоить $ bar этим результатам? Или есть более разумный способ сделать все это в одном запросе (например, $ row ['ContactName'] Первая итерация, $ row ['ContactName'] Вторая итерация)? Совет здесь будет высоко ценится.

И это все! Я старался быть максимально ясным и очень ценю любую помощь или совет, который вы можете дать. Заранее спасибо.

################################################## ########################РЕДАКТИРОВАТЬ

Мой запрос в настоящее время представляет собой объединение запросов, предоставленных Cularis и Symcbean:

SELECT *
        FROM    (
            profiles
            INNER JOIN prm_breedgender
                ON profiles.ProfileGenderID = prm_breedgender.BreedGenderID
            LEFT JOIN contacts AS owners
                ON profiles.ProfileOwnerID = owners.ContactID
            INNER JOIN prm_breedcolour
                ON profiles.ProfileAdultColourID = prm_breedcolour.BreedColourID
            )
        LEFT JOIN contacts AS breeders
            ON profiles.ProfileBreederID = breeders.ContactID
        ORDER BY prm_breedgender.BreedGender ASC, profiles.ProfileYearOfBirth ASC $limit

Это работает, поскольку результаты упорядочены так, как я надеялся: то есть по возрасту и полу. Тем не менее, я не могу заставить псевдоним работать в отношении запросов контактов (заводчик и владелец). Никаких ошибок не отображается, а также нет владельцев или заводчиков. Любые дальнейшие разъяснения по этому поводу будут очень благодарны.

P.s. Я удалил псевдоним, предоставленный последнему LEFT JOIN по примеру Symcbean, так как я не мог заставить результирующий оператор ORDER BY работать на меня - я уверен, что это моя вина. Тем не менее, это работает сейчас, хотя это может быть причиной проблемы с запросом контактов.

Ответы [ 3 ]

2 голосов
/ 27 июля 2011

Чтобы еще больше расширить сказанное @ularis, группируйте по группам для агрегации до самого низкого уровня критериев "группировки". Например, и я не делаю по вашим конкретным таблицам, но вы увидите влияние. Скажем, вы хотите показать страницу, сгруппированную по породе. Затем пользователь выбирает породу и видит все записи этой породы.

PonyID   ProfileGenderID    Breeder
1        1                  1
2        1                  1
3        2                  2
4        3                  3
5        1                  2
6        1                  3
7        2                  3

Assuming your Gender table is a lookup where ex:
BreedGenderID   Description
1               Stallion
2               Mare
3               Geldings

ВЫБРАТЬ * ИЗ профилей ВНУТРЕННЕЕ ПРИСОЕДИНЕНИЕ prm_breedgender ON profiles.ProfileGenderID = prm_breedgender.BreedGenderID

select
      BG.Description,
      count(*) as CountPerBreed
   from
      Profiles P
         join prm_BreedGender BG
            on p.ProfileGenderID = BG.BreedGenderID
   group by
      BG.Description
   order by 
      BG.Description

приведет к чему-то подобному (счетчик только по совпадению)

Description   CountPerBreed
Geldings      1
Mare          2
Stallion      4

измените предложение "order by" на "order by CountsPerBreed Desc" (для убывания), и вы получите

Description   CountPerBreed
Stallion      4
Mare          2
Geldings      1

Расширить, если вы хотите, чтобы агрегаты были разбиты по заводчикам ... Лучше всего группировать по всем НЕ НЕГРУЖАЕМЫМ (таким как MIN (), MAX (), AVG (), COUNT). (), СУММА () и т. Д.)

select
      BG.Description,
      BR.BreaderName,
      count(*) as CountPerBreed
   from
      Profiles P

         join prm_BreedGender BG
            on p.ProfileGenderID = BG.BreedGenderID

         join Breeders BR
            on p.Breeder = BR.BreaderID
   group by
      BG.Description,
      BR.BreaderName
   order by 
      BG.Description

приведет к чему-то вроде (счетные значения будут совпадать только последовательно)

Description   BreaderName  CountPerBreed
Geldings      Bill         1
Mare          John         1
Mare          Sally        1
Stallion      George       2
Stallion      Tom          1
Stallion      Wayne        1

Как видите, чем больше гранулярности вы предоставляете группе, тем меньше агрегация для этого уровня.

В противном случае ваши условия присоединения будут понятны из того, что вы предоставили. Надеемся, что этот пример четко показывает, что будет делать процесс запроса. Ваша группа не обязательно должна совпадать с окончательным порядком ... просто это видно, поэтому кто-то, смотрящий на результаты, не пытается угадать, как организованы данные.

В вашей выборке у вас был заказ к году рождения. Выполняя агрегацию, вы никогда не будете иметь конкретный год рождения отдельного пони, чтобы упорядочить его по ... UNLESS .... Вы включили YEAR (ProfileYearOfBirth) в качестве BirthYear в качестве столбца и включили его в свою группу. .. Например, наличие 100 пони в возрасте 1 года и 37 лет в возрасте 2 лет для данной породы.

2 голосов
/ 27 июля 2011

GROUP в терминах SQL означает использование агрегатных функций над группой записей. Я думаю, что вы хотите, это порядок по полу:

ORDER BY prm_breedgender.BreedGender ASC, profiles.ProfileYearOfBirth ASC $limit

Это выведет всех жеребцов и т. Д. Рядом друг с другом.

Чтобы также получить контакт заводчиков, вам нужно снова присоединиться к таблице контактов, используя псевдоним:

LEFT JOIN contacts AS owners
            ON profiles.ProfileOwnerID = owners.ContactID

LEFT JOIN contacts AS breeders
            ON profiles.ProfileBreederID = breeders.ContactID
0 голосов
/ 27 июля 2011

Было бы полезно, если бы вы предоставили подробную информацию о структуре таблицы и приблизительном количестве строк.Использование '*' для SELECT также является грязной практикой, и позже у вас возникнут проблемы (см. Ниже).

Какая версия MySQL?

, очевидно, необходимо реорганизоватьмой запрос для включения GROUP в мое основное предложение SELECT

Не обязательно начиная с v4 (? IIRC), вы можете просто заключить свой запрос в консолидирующий выбор (но переместить предел во внешний выбор:

SELECT ProfileGenderID, COUNT(*)
FROM (
 [your query without the LIMIT]
) ilv
GROUP BY ProfileGenderID
LIMIT $limit;

(обратите внимание, что вы не можете ЗАКАЗАТЬ ilv.ProfileYearOfBirth, поскольку это не выбранный столбец / группа по выражению)

Сколько записей / столбцов у вас в prm_breedgender? Это простоЖеребцы, Марес, Меринс ... Как вы думаете, этот список может измениться? У вас есть пони с несколькими полами? Я подозреваю, что этот домен будет лучше представлен перечислением в таблице профилей.

с целью составления списка владельцев и заводчиков пони,

Используя предложенный вами код, вы получите только возвращенные экземпляры, в которых владелеци заводчик одинаковы!Вам нужно добавить второй экземпляр таблицы контактов с другим псевдонимом, чтобы получить их все, например

SELECT *
FROM (
  SELECT *
  FROM profiles
    INNER JOIN prm_breedgender
        ON profiles.ProfileGenderID = prm_breedgender.BreedGenderID
    LEFT JOIN contacts ownerContact
        ON profiles.ProfileOwnerID = ownerContact.ContactID
    INNER JOIN prm_breedcolour
        ON profiles.ProfileAdultColourID = prm_breedcolour.BreedColourID
) ilv LEFT JOIN contacts breederContact
     ON ilv.ProfileBreederID = breederContact.ContactID
ORDER BY ilv.ProfileYearOfBirth ASC $limit
...