|
@@ -362,8 +362,84 @@ SELECT *
|
|
|
person_id = 1;
|
|
|
```
|
|
|
|
|
|
+Однако, если мы сделаем запрос для выборки всех людей с их номерами телефонов, мы получим *не всех людей*:
|
|
|
+```mysql
|
|
|
+SELECT *
|
|
|
+ FROM
|
|
|
+ person,
|
|
|
+ person_to_phone,
|
|
|
+ phone
|
|
|
+ WHERE
|
|
|
+ person.id = person_to_phone.person_id AND
|
|
|
+ person_to_phone.phone_id = phone.id;
|
|
|
+```
|
|
|
+...a только тех, у кого *есть номера телефонов*.
|
|
|
+
|
|
|
### Несколько таблиц, `RIGHT` и `LEFT` `JOIN` (`OUTER JOIN`)
|
|
|
+Для решения вышеозначенной проблемы есть запросы `LEFT` и `RIGHT JOIN`:
|
|
|
+```mysql
|
|
|
+SELECT *
|
|
|
+ FROM
|
|
|
+ person LEFT JOIN person_to_phone ON (person.id = person_to_phone.person_id)
|
|
|
+ LEFT JOIN phone ON (person_to_phone.phone_id = phone.id);
|
|
|
+```
|
|
|
+Запрос выше показывает *всех* пользователей, даже тех, у кого телефонов *нет*. Недостающие поля заполняются `NULL`
|
|
|
+```mysql
|
|
|
+SELECT *
|
|
|
+ FROM
|
|
|
+ person RIGHT JOIN person_to_phone ON (person.id = person_to_phone.person_id)
|
|
|
+ RIGHT JOIN phone ON (person_to_phone.phone_id = phone.id);
|
|
|
+```
|
|
|
+Этот запрос, наоборот, показывает все телефоны, один из который не принадлежит никому из пользователей.
|
|
|
|
|
|
### `GROUP BY` и агрегация
|
|
|
+Нередко возникает задача, когда требуется не выводить все присоединенные записи, а произвести над ними определенные действия и вывести один результат
|
|
|
+для каждой "родительской" записи. Например, подсчитаем количество телефонов у каждого пользователя
|
|
|
+
|
|
|
+```mysql
|
|
|
+SELECT
|
|
|
+ person.id,
|
|
|
+ person.surname,
|
|
|
+ count(phone.id)
|
|
|
+ FROM
|
|
|
+ person
|
|
|
+ LEFT JOIN person_to_phone ON (person.id = person_to_phone.person_id)
|
|
|
+ LEFT JOIN phone ON (person_to_phone.phone_id = phone.id)
|
|
|
+ GROUP BY
|
|
|
+ person.id;
|
|
|
+```
|
|
|
+
|
|
|
+Так же мы можем получить все номера телефонов в удобном для `explode` виде:
|
|
|
+
|
|
|
+```mysql
|
|
|
+SELECT
|
|
|
+ person.id,
|
|
|
+ person.surname,
|
|
|
+ COUNT(phone.id),
|
|
|
+ GROUP_CONCAT(phone.phone)
|
|
|
+ FROM
|
|
|
+ person
|
|
|
+ LEFT JOIN person_to_phone ON (person.id = person_to_phone.person_id)
|
|
|
+ LEFT JOIN phone ON (person_to_phone.phone_id = phone.id)
|
|
|
+ GROUP BY
|
|
|
+ person.id;
|
|
|
+```
|
|
|
+
|
|
|
+Или подсчитать средний возраст пользователей тех или иных типов телефонов:
|
|
|
+
|
|
|
+```mysql
|
|
|
+SELECT
|
|
|
+ phone.phone_type,
|
|
|
+ AVG(TIMESTAMPDIFF(YEAR, person.date_of_birth, CURDATE()))
|
|
|
+ FROM
|
|
|
+ phone
|
|
|
+ LEFT JOIN person_to_phone ON (phone.id = person_to_phone.phone_id)
|
|
|
+ LEFT JOIN person ON (person_to_phone.person_id = person.id)
|
|
|
+ GROUP BY
|
|
|
+ phone_type;
|
|
|
+```
|
|
|
+
|
|
|
+Для понимания, как именно работает `GROUP BY`, попробуйте его убрать и отсортировать выборку по полю, которое было в GROUP BY. Таким образом вы увидите
|
|
|
+*все* записи сгруппированными благодаря сортировке. СУБД работает схожим образом.
|
|
|
|
|
|
### `AS` и древовидные структуры данных в реляционных СУБД.
|