Wonderful
@Wonderful

Как сортировать динамические данные в MySQL? Опасен ли filesort?

Есть таблица пользователей, фруктов и таблица "отношений":
CREATE TABLE IF NOT EXISTS `relations` (
  `user_id` int(11) unsigned NOT NULL,
  `fruit_id` int(11) unsigned NOT NULL,
  UNIQUE KEY `xox` (`fruit_id`,`user_id`),
  KEY `uid` (`user_id`,`fruit_id`)
) ENGINE=InnoDB;

Fruit relations:
user_id, fruit_id
1        1
1        2
1        5
2        1
2        2
2        4
3        3
3        1

Требуется сравнить фрукты пользователя (id: 1) с фруктами остальных пользователей. Узнать у кого из них наибольшее число совпадений и отсортировать в порядке убывания:
user_id,  count
1         3
2         2
3         1

Запрос:
SELECT user_id, COUNT(*) AS count FROM relations
WHERE fruit_id IN (SELECT fruit_id  FROM relations WHERE user_id=1) 
GROUP BY user_id
HAVING count>=2

2 - минимальное кол-во совпадений, на будущее.

Почитал про оптимизацию с оператором IN (Optimizing Subqueries with EXISTS Strategy), решел "оптимизировать":
SELECT user_id, COUNT(*) AS count FROM relations r
WHERE EXISTS (SELECT 1 FROM relations WHERE user_id=1 AND r.fruit_id=fruit_id)
GROUP BY user_id
HAVING count>=2


Всё вроде отлично, explain:
id 	select_type 	 	table 	 	 type 	possible_keys 	key 	key_len 	ref 	 	 	rows 	Extra
1 	PRIMARY 	 	r 	 	 index 	NULL 	 	uid 	8 	 	NULL 	 	 	15 	Using where; Using index
2 	DEPENDENT SUBQUERY 	relations 	eq_ref 	xox,uid 	xox 	8 	 	r.fruit_id,const 	1 	Using where; Using index


Но, нужно же отсортировать в порядке убывания. Если бы данных было мало и не нужен бы был постраничный вывод, то легко можно было бы отсортировать с помощью языка программирования.

Поэтому пришла такая простая идея:
SELECT  x.user_id, x.count
FROM (
     SELECT user_id, COUNT(*) AS count FROM relations r
     WHERE EXISTS (SELECT 1 FROM relations WHERE user_id=1 AND r.fruit_id=fruit_id)
     GROUP BY user_id
     HAVING count>=2
     ) x
ORDER BY x.count DESC

Explain:
id 	select_type 	 	table 	 	type 	possible_keys 	key 	key_len 	ref 	 	rows 	Extra
1 	PRIMARY 	 	<derived2> 	ALL 	NULL 	 	NULL 	NULL 	 	NULL 	 	3 	Using filesort
2 	DERIVED 	 	r 	 	index 	NULL 	 	uid 	8 	 	NULL 	 	15 	Using where; Using index
3 	DEPENDENT SUBQUERY 	relations 	eq_ref 	xox,uid 	xox 	8 	 	r.int_id 	1 	Using index


У меня, собственно, вопросы:

1. Опасен ли этот filesort? Если потом в таблице будет, скажем, 50 миллионов полей или больше?
2. Можно ли как-то сделать то, что я хочу без using temporary table / using filesort?

Спасибо.

UPD:
Не назвал бы это тестированием, просто заполнил таблицу:
20k записей, в среднем 0.05 сек
120k - 0.20 сек
1100k - 2.9 сек.
Да уж, совсем не радует. Можно было бы изменить структуру таблиц, но как, при таком подсчете и сортировке - не понимаю. Есть предложения, как ещё это можно реализовать?
Весь смысл в том, чтобы сначала показать наибольшие совпадения..
  • Вопрос задан
  • 2414 просмотров
Пригласить эксперта
Ответы на вопрос 2
Melkij
@Melkij
PostgreSQL DBA
Смотря сколько до filesort доходит данных и каких. В выводе explain не различается filesort в памяти и на диске.
Запишите эти 50 лямов строк и посмотрите, как они себя ведут. Для таблицы связей значение всё равно игрушечное.
Ответ написан
Комментировать
ivinnic
@ivinnic
Full-Stack - подустал
1. Да опасен. Запрос уже будет тормозить при 10k
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы