[image]

SQL запрос на пересечение внутри одной таблицы

Теги:Linux
 
+
-
edit
 

KRoN73
Balancer

администратор
★★★★★
Linux
Есть таблица, пусть, для простоты, вида:
keyword_id, object_id

Т.е. таблица привязок ключевых слов к объектам.

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

Первое, что приходит в голову, конструкция типа

SELECT DISTINCT object_id FROM tab t0
INNER JOIN tab t1 ON t1.object_id = t0.object_id AND t1.keyword_id = kw1
INNER JOIN tab t2 ON t2.object_id = t0.object_id AND t2.keyword_id = kw2
INNER JOIN tab t3 ...
WHERE t0.keyword_id = t0;

Но это кошмар. И работает ужасно медленно. На весьма мощной машине запрос по двум ключевым словам выполняется 6-7 секунд.

Если подумать, то приходит в голову другой вариант. В духе:

SELECT object_id FROM tab WHERE keyword_id in (kw1,kw2,...,kwN) GROUP BY object_id HAVING COUNT(*) = N;

Этот вариант уже заметно красивее и достаточно быстро работает (0,1сек в моём случае). Однако, всё равно остаются 0,1 сек. и файловая сортировка в explain.

Нет ли ещё какого-то более изящного решения?

[/p][/p][/p][/p][/p][/p][/p][/p][/p]

// Транслировано с
http://www.linux.org.ru/forum/development/5372658
   
+
-
edit
 

Luchnik

старожил
★★
KRoN73> SELECT object_id FROM tab WHERE keyword_id in (kw1,kw2,...,kwN) GROUP BY object_id HAVING COUNT(*) = N;

Будет правильно если комбинация keyword_id, object_id уникальна.
   
+
-
edit
 

Balancer

администратор
★★★★★
Luchnik> Будет правильно если комбинация keyword_id, object_id уникальна.

Ну, как бы, поразумевается. Смысла нет прописывать дважды одно ключевое слово на объект :)

...

Вообще, это для Ключевые слова // АвиаПорт.Ru и, со временем, для Тэги Хотя, в силу ряда специфических моментов, последняя ссылка и со старым вариантом не особо тормозит.
   6.0.472.636.0.472.63
+
-
edit
 

Mishka

модератор
★★★
KRoN73> Есть таблица, пусть, для простоты, вида:
KRoN73> keyword_id, object_id


Мой пост вчерашний пропал. :) Попробую восстановить.



Долго, т.к. там достаточно большое Декартово произведение выходит. Особенно, если последнее под WHERE стоит таблица.

А так в последнем варианте не совсем понятно присутствие HAVING COUNT — тебе надо просто слова найти или со счётчиком определённым?

SELECT DISTINCT object_id
FROM tab
WHERE keyword_id in (kw1,kw2,...,kwN);


Сработает нормально.

Если же счётчик нужен, то

SELECT object_id, COUNT(object_id)
FROM tab
WHERE keyword_id in (kw1,kw2,...,kwN)
GROUP BY object_id;

Но это общий счётчик того факта, сколька на данный объект ссылались ключевые слова. Своеобразный счётчик синонимов.

PS В первом случае вместо join-ов можно применить union. :) Будет похоже по скорости на вариант с IN.

PPS Только не забывай, что есть ограничение на размер sQL Statement. ИМХО, большинство СУБД позволяют это регулировать.
   3.6.103.6.10
+
-
edit
 

Balancer

администратор
★★★★★
Mishka> А так в последнем варианте не совсем понятно присутствие HAVING COUNT — тебе надо просто слова найти или со счётчиком определённым?

Смотри. У меня есть объекты (No), есть ключевые слова (Nk) и есть таблица с привязками (X = 0 .. No*Nk). Я дёргаю все привязки, которые связывают любые сочетания всех объектов и нужных мне ключевых слов. В том и только том случае, если к объекту привязаны все ключевые слова, записей с этим объектом будет Nk штук. Соответственно, я результат группирую по объектам, и если в группировке COUNT(*) == Nk, то данному объекту привязаны все искомые ключевые слова.

Mishka> SELECT DISTINCT object_id
Mishka> FROM tab
Mishka> WHERE keyword_id in (kw1,kw2,...,kwN);
Mishka> Сработает нормально.

Это объединение множеств ключевых слов. А мне нужно пересечение. Мне нужны объекты, к которым привязаны все ключевые слова. Например, новости, с которыми связаны «Аэрофлот» И «контракты». У тебя же получается «Аэрофлот» ИЛИ «контракты».
   6.0.472.636.0.472.63
+
-
edit
 

Balancer

администратор
★★★★★
На Авиабазе в Тэгах - там совсем было начерно сделано :D Дёргаются Nk массивов с объектами, привязанными к конкретным одиночным ключевым словам. А потом, средствами PHP, ищется пересечение этих массивов. На Авиабазе мало ключевых слов и мало топиков, к которым они привязаны, так что результат был достаточно шустрый. А на Авиапорте - в 6 раз больше новостей, чем у нас топиков и ключевые слова есть под каждую новость. В итоге генерация страниц затянулась до нескольких секунд. В самых запущенных случаях - до 10 секунд.

Со вторым вариантом время генерации стало порядка 0,2..0,3 сек. Страницы не самые посещаемые, так что пока хватит :)
   6.0.472.636.0.472.63
+
-
edit
 

Mishka

модератор
★★★
Balancer> если к объекту привязаны все ключевые слова,

Я трактовал твои слова про все ненмого расширено.


Тогда вариант с HAVING работает нормально.
   3.6.103.6.10

в начало страницы | новое
 
Поиск
Настройки
Твиттер сайта
Статистика
Рейтинг@Mail.ru