2015-11-25 5 views
0

Существует простое требование, которое запрашивает сумму отношения Six Degrees из таблицы Friend.Оптимизировать запрос MySQL с большим предложением in()

Структура Friend такова:

+----------+---------+------+-----+---------+----------------+ 
| Field | Type | Null | Key | Default | Extra   | 
+----------+---------+------+-----+---------+----------------+ 
| id  | int(11) | NO | PRI | NULL | auto_increment | 
| userId | int(11) | NO | MUL | NULL |    | 
| friendId | int(11) | NO |  | NULL |    | 
+----------+---------+------+-----+---------+----------------+ 

Предположим, я хочу знать шесть степеней количество Взаимоотношение userId:1, и я записал шесть запросов, как этот
SELECT friendId FROM Friend WHERE userId = 1, чтобы получить один градусные друзей ,

Затем выполнить
SELECT friendId FROM Friend WHERE userId in (/*above query result*/)
пять раз.

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

Существует большая вероятность того, что размер отношений Six Degrees пользователя 1 больше шестизначных чисел, хотя у него есть только два друга в отношениях с одной степенью.

Количество элементов в предложении IN экспоненциально.

Затем шесть запросов занимают более одной минуты, чтобы получить результат.

Как оптимизировать эту ситуацию?

+0

разместить Ваш запрос –

+0

создать временную таблицу с индексом на объединенном столбце, а затем использовать JOIN вместо IN – Borjante

+0

Этих может представлять интерес: [Задача, как реализовать алгоритм для шести степеней разделения?] (http://stackoverflow.com/questions/2076715/challenge-how-to-implement-an-algorithm-for-six-degree -of-разделение) – jpw

ответ

0

Вы можете использовать subqueries и посмотреть, достаточно ли оптимизатор MySQL, чтобы переписать их как объединения (как это обычно бывает).

Но на самом деле РСУБД не подходит для задачи. Лучше посмотрите на базы данных на основе графов. См. Например, this question.

0

Создайте временную таблицу для хранения промежуточных результатов, и JOIN вместо IN:

DROP TEMPORARY TABLE IF EXISTS tmp_friends; 
CREATE TEMPORARY TABLE `tmp_friends` (
    `id` INT UNSIGNED NOT NULL, 
    PRIMARY KEY (`id`) 
); 

INSERT INTO tmp_friends VALUES(<id of the given user>); 

#run this 6 times 
INSERT IGNORE INTO tmp_friends 
SELECT f.userId 
FROM tmp_friends t 
JOIN Friend f ON f.friendId = t.id 

SELECT f.* 
FROM tmp_friends t 
JOIN Friend f ON f.userId = t.id 
Смежные вопросы