SQLinfo.ru - Все о MySQL Webew.ru: теория и практика веб-технологий

Форум пользователей MySQL

Задавайте вопросы, мы ответим

Вы не зашли.

#1 23.09.2010 13:34:25

mihim
Участник
Зарегистрирован: 23.09.2010
Сообщений: 10

Запрос с GROUP BY

Дано: 2 таблицы - towns(id, title), vacancies(id, town_id).
Надо получить: список названий всех городов из towns с количеством вакансий в этом городе.

Следующий запрос выбирает только города, для которых есть записи в vacancies.
SELECT towns.title, COUNT(vacancies.id) FROM towns, vacancies WHERE towns.id = vacancies.town_id GROUP BY towns.title

Подскажите, пожалуйста, как получить ВСЕ города? Заранее очень признателен.

Неактивен

 

#2 23.09.2010 13:52:54

vasya
Архат
MySQL Authorized Developer
Откуда: Орел
Зарегистрирован: 07.03.2007
Сообщений: 5842

Re: Запрос с GROUP BY

используйте left join
http://dev.mysql.com/doc/refman/5.1/en/join.html

Неактивен

 

#3 23.09.2010 14:33:04

mihim
Участник
Зарегистрирован: 23.09.2010
Сообщений: 10

Re: Запрос с GROUP BY

Vasya, был у меня вариант с left join, не устроила скорость работы (10000 записей за 1,5 сек)


SELECT towns.title, COUNT(vacancies.id)
FROM towns left join vacancies
on towns.id = vacancies.town_id
GROUP BY towns.title
 

Зато на другом форуме посоветовали очень недурственный вариант, который выполняется за 0,02 сек:

SELECT towns.title, ifnull(cou, 0) cou
from towns left join
(
SELECT town_id, COUNT(*) cou
FROM vacancies
GROUP BY town_id) vac
on towns.id = vac.town_id
 

Неактивен

 

#4 23.09.2010 16:26:37

paulus
Администратор
MySQL Authorized Developer and DBA
Зарегистрирован: 22.01.2007
Сообщений: 6757

Re: Запрос с GROUP BY

Да, группировать по имени города — идея ужасная. Но подзапрос
в данном случае тоже лишний.

Неактивен

 

#5 23.09.2010 19:01:37

mihim
Участник
Зарегистрирован: 23.09.2010
Сообщений: 10

Re: Запрос с GROUP BY

paulus, каким именно на ваш взгяд должен быть запрос в данной ситуации?

Неактивен

 

#6 23.09.2010 22:43:43

paulus
Администратор
MySQL Authorized Developer and DBA
Зарегистрирован: 22.01.2007
Сообщений: 6757

Re: Запрос с GROUP BY

Подойдет тот, что у Вас написан, но GROUP BY towns.id.
Нужны ключики на vacancies (town_id, id) и towns (id).

Неактивен

 

#7 24.09.2010 10:00:32

mihim
Участник
Зарегистрирован: 23.09.2010
Сообщений: 10

Re: Запрос с GROUP BY

paulus, насколько я Вас понял запрос должен выглядеть так:

SELECT towns.title, COUNT(vacancies.id)
FROM towns LEFT JOIN vacancies ON towns.id = vacancies.town_id
GROUP BY towns.id

Я действительно промухал ключ на vacancies.town_id, проставил и время работы уменьшилось. Теперь 0,07 сек, вместо 1,15.
Но вместе с тем уменьшилось время работы и варианта с вложенным запросом 0,005 сек. То есть он все равно работает намного быстрее.
Не могли бы Вы объяснить чем плох вариант с вложенным запросом?

Неактивен

 

#8 26.09.2010 19:37:17

paulus
Администратор
MySQL Authorized Developer and DBA
Зарегистрирован: 22.01.2007
Сообщений: 6757

Re: Запрос с GROUP BY

А EXPLAIN от обоих вариантов покажите?

Неактивен

 

#9 27.09.2010 10:52:29

mihim
Участник
Зарегистрирован: 23.09.2010
Сообщений: 10

Re: Запрос с GROUP BY

Количество записей:
vacancies 14406
towns 25

Сгенерил новый тестовый набор записей для vacancies(более равномерный). Теперь примерно по 600 записей на каждый город (раньше все записи приходились на 5 городов).

Индексы towns: id(PRIMARY).
Индексы vacancies: id(PRIMARY), tonw_id(INDEX).

Время выполнения:
без вложенного запроса 0,035 сек.
с вложенным запросом 0,007 сек.

Результаты EXPLAIN в прикрепленном файле.

Отредактированно mihim (27.09.2010 11:55:21)


Прикрепленные файлы:
Attachment Icon Both.jpg, Размер: 76,123 байт, Скачано: 395

Неактивен

 

#10 27.09.2010 12:20:46

paulus
Администратор
MySQL Authorized Developer and DBA
Зарегистрирован: 22.01.2007
Сообщений: 6757

Re: Запрос с GROUP BY

А теперь посмотрите, какой индекс я предложил сделать на vacancies wink

И, если после добавления, не будет лопать индекс целиком, я бы
добавил ORDER BY vacancies.town_id.

Неактивен

 

#11 27.09.2010 14:16:51

mihim
Участник
Зарегистрирован: 23.09.2010
Сообщений: 10

Re: Запрос с GROUP BY

paulus, насчет индексов сначала не так Вас понял. Сделал индекс по двум полям (vacancies.town_id, vacancies.id). Время выполнения уменьшилось с 0,035 сек до 0,02 сек. ORDER BY vacancies.town_id на ситуацию никак не повлиял.
При этом вариант с вложенным запросом все равно работает в 3 раза быстрее (0,007 сек).

Отредактированно mihim (27.09.2010 14:18:01)

Неактивен

 

#12 27.09.2010 18:14:09

paulus
Администратор
MySQL Authorized Developer and DBA
Зарегистрирован: 22.01.2007
Сообщений: 6757

Re: Запрос с GROUP BY

Не повлиял — это Вы судите по EXPLAIN?

Неактивен

 

#13 28.09.2010 10:19:33

mihim
Участник
Зарегистрирован: 23.09.2010
Сообщений: 10

Re: Запрос с GROUP BY

И по EXPLAIN (картинка прицеплена) и по скорости работы.


Прикрепленные файлы:
Attachment Icon ORDER_BY.jpg, Размер: 66,315 байт, Скачано: 468

Неактивен

 

#14 28.09.2010 18:36:17

paulus
Администратор
MySQL Authorized Developer and DBA
Зарегистрирован: 22.01.2007
Сообщений: 6757

Re: Запрос с GROUP BY

И правда, он странно выбирает sad

Неактивен

 

#15 29.09.2010 13:54:13

avgsoft
Завсегдатай
Зарегистрирован: 23.09.2010
Сообщений: 36

Re: Запрос с GROUP BY

да все нормально работает.
в первом запросе из-за того что count и group by оперируют данными из разных таблиц и появляется using temporary..  и using filesort.
во втором запросе те самые using temporary и filesort появляются из-за требования отсортировать выборку по vacancies.town_id. на мой взгляд лишнее требование, т.к. group by по умолчанию обеспечит сортировку

Неактивен

 

#16 29.09.2010 14:31:49

mihim
Участник
Зарегистрирован: 23.09.2010
Сообщений: 10

Re: Запрос с GROUP BY

Спасибо за помощь всем откликнувшимся!
И все же кто-нибудь может объяснить почему вариант с вложенным запросом, имея далеко неидеальный EXPLAIN, работает в 3 раза быстрее обычного запроса с оптимизированным EXPLAIN?

Неактивен

 

#17 29.09.2010 16:17:39

avgsoft
Завсегдатай
Зарегистрирован: 23.09.2010
Сообщений: 36

Re: Запрос с GROUP BY

SELECT towns.title, COUNT(vacancies.id)
FROM vacancies
left join towns
on towns.id = vacancies.town_id
GROUP BY vacancies.town_id

вот такой запрос не пробовали? он по логике исполнения является аналогом "запроса с подзапросом".  должен показать такое же время исполнения.
а по поводу подзапроса в принципе - сложности начнутся когда результат подзапроса перестанет помещаться в память отведенную под временные таблицы.  насчет запроса с оптимизированным explain  - я его не нашел. может смотрю не там?

Неактивен

 

#18 29.09.2010 16:18:45

paulus
Администратор
MySQL Authorized Developer and DBA
Зарегистрирован: 22.01.2007
Сообщений: 6757

Re: Запрос с GROUP BY

Ррр, ну почему приходится делать тестовые данные, чтобы показать, что
на самом деле всё хорошо? sad

Код:

mysql> SHOW CREATE TABLE towns\G
*************************** 1. row ***************************
       Table: towns
Create Table: CREATE TABLE `towns` (
  `id` int(11) DEFAULT NULL,
  `title` varchar(20) DEFAULT NULL,
  KEY `id` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

mysql> SHOW CREATE TABLE vacancies\G
*************************** 1. row ***************************
       Table: vacancies
Create Table: CREATE TABLE `vacancies` (
  `id` int(11) DEFAULT NULL,
  `town_id` int(11) DEFAULT NULL,
  KEY `town_id` (`town_id`,`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

mysql> SELECT * FROM towns;
+------+-------------------------------+
| id   | title                         |
+------+-------------------------------+
|    1 | Москва                        |
|    2 | Санкт-Петербург               |
|    3 | Владимир                      |
|    4 | Новгород                      |
+------+-------------------------------+
4 rows in set (0.00 sec)

mysql> SELECT * FROM vacancies;
+------+---------+
| id   | town_id |
+------+---------+
|    1 |       1 |
|    2 |       1 |
|    3 |       2 |
|    4 |       3 |
+------+---------+
4 rows in set (0.00 sec)

mysql> EXPLAIN SELECT title, COUNT(v.id) FROM towns t LEFT JOIN vacancies v ON t.id = v.town_id GROUP BY t.id ORDER BY NULL;
+----+-------------+-------+------+---------------+---------+---------+-----------+------+-----------------+
| id | select_type | table | type | possible_keys | key     | key_len | ref       | rows | Extra           |
+----+-------------+-------+------+---------------+---------+---------+-----------+------+-----------------+
|  1 | SIMPLE      | t     | ALL  | NULL          | NULL    | NULL    | NULL      |    4 | Using temporary |
|  1 | SIMPLE      | v     | ref  | town_id       | town_id | 5       | test.t.id |    1 | Using index     |
+----+-------------+-------+------+---------------+---------+---------+-----------+------+-----------------+
2 rows in set (0.00 sec)

mysql> SELECT title, COUNT(v.id) FROM towns t LEFT JOIN vacancies v ON t.id = v.town_id GROUP BY t.id ORDER BY NULL;
+-------------------------------+-------------+
| title                         | COUNT(v.id) |
+-------------------------------+-------------+
| Москва                        |           2 |
| Санкт-Петербург               |           1 |
| Владимир                      |           1 |
| Новгород                      |           0 |
+-------------------------------+-------------+
4 rows in set (0.00 sec)

Неактивен

 

#19 29.09.2010 16:21:59

avgsoft
Завсегдатай
Зарегистрирован: 23.09.2010
Сообщений: 36

Re: Запрос с GROUP BY

а как же using temporary...? вообще говоря это не очень хорошо.

Неактивен

 

#20 29.09.2010 16:28:07

avgsoft
Завсегдатай
Зарегистрирован: 23.09.2010
Сообщений: 36

Re: Запрос с GROUP BY

попробуйте загнать в vacancies сильно больше данных(сотни тысяч, миллионы)  и "Using temporary" обязательно окажет свое пагубное влияние на время исполнения

Неактивен

 

#21 29.09.2010 16:29:40

paulus
Администратор
MySQL Authorized Developer and DBA
Зарегистрирован: 22.01.2007
Сообщений: 6757

Re: Запрос с GROUP BY

Попробуйте загнать сотни тысяч данных в выборку без WHERE, и Вы поймете,
почему Ваше требование не важно в данном случае wink

Но раз уж Вы придираетесь, то вот так Вас должно устроить:

Код:

mysql> ALTER TABLE towns DROP KEY id, ADD PRIMARY KEY(id);
Query OK, 4 rows affected (0.38 sec)
Records: 4  Duplicates: 0  Warnings: 0

mysql> EXPLAIN SELECT title, COUNT(v.id) FROM towns t LEFT JOIN vacancies v ON v.town_id =  t.id GROUP BY t.id ORDER BY NULL;
+----+-------------+-------+-------+---------------+---------+---------+-----------+------+-------------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref       | rows | Extra       |
+----+-------------+-------+-------+---------------+---------+---------+-----------+------+-------------+
|  1 | SIMPLE      | t     | index | NULL          | PRIMARY | 4       | NULL      |    4 |             |
|  1 | SIMPLE      | v     | ref   | town_id       | town_id | 5       | test.t.id |    1 | Using index |
+----+-------------+-------+-------+---------------+---------+---------+-----------+------+-------------+
2 rows in set (0.00 sec)

Неактивен

 

#22 29.09.2010 16:52:34

avgsoft
Завсегдатай
Зарегистрирован: 23.09.2010
Сообщений: 36

Re: Запрос с GROUP BY

да нет, так тоже не особо весело. я бы даже сказал совсем не весело. подзапрос качественно лучше.

Неактивен

 

#23 29.09.2010 19:17:38

paulus
Администратор
MySQL Authorized Developer and DBA
Зарегистрирован: 22.01.2007
Сообщений: 6757

Re: Запрос с GROUP BY

Вы правда в это верите? smile

Но если Вы автор решения с подзапросом — то, разумеется, лучше: это
решение было получено куда быстрее wink

Неактивен

 

#24 29.09.2010 22:54:33

mihim
Участник
Зарегистрирован: 23.09.2010
Сообщений: 10

Re: Запрос с GROUP BY

paulus, в EXPLAIN все красиво, но хоть убейте меня, скорость работы варианта с подзапросом выше в 3 раза. На 130 000 записей в vacancies и 25 городах -  0,07 против 0,19 сек smile В теории с подзапросом должно работать медленнее, но на практике всё иначе...

Отредактированно mihim (29.09.2010 22:58:50)

Неактивен

 

#25 29.09.2010 23:23:57

paulus
Администратор
MySQL Authorized Developer and DBA
Зарегистрирован: 22.01.2007
Сообщений: 6757

Re: Запрос с GROUP BY

Я ж не против, я же давно уже говорю, что раз рабочее решение есть —
надо использовать его. В теории нигде не сказано, что подзапросы
должны работать медленнее. На практике же это почти всегда так:
MySQL определяет подзапрос зависимым, и выполняет его много раз
вместо того, чтобы сделать это единожды. В Вашем же случае это не так,
поэтому скорости с подзапросом и без должны быть сравнимы. Что мы,
собственно, и получили.

Неактивен

 

Board footer

Работает на PunBB
© Copyright 2002–2008 Rickard Andersson