Сложный запрос, три таблицы

12
Станислав
На сайте с 27.12.2009
Offline
237
493

Нужно вывести похожие записи по тегам, есть три таблицы

Первыя таблицы с постами

TABLE
id | title

Вторая таблица с тегами

TABLE_2
id | tag

Третья таблица связка

TABLE_3
tag_id | post_id

Где tag_id - TABLE_2.id

А post_id - TABLE.id

Есть так же запрос вида

select w1.* , w2.*
from TABLE w1
join
(SELECT count(w.id) as t_num, w.id
FROM TABLE w
join TABLE_3 r2 on w.id = r2.post_id
join TABLE_3 r on r2.tag_id = r.tag_id
WHERE r.post_id = '10280' AND w.id != '10280'
GROUP BY w.id
ORDER BY count(w.id) DESC
LIMIT 1, 4) w2 on w2.id = w1.id

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

Мы там, где рады нас видеть.
IL
На сайте с 20.04.2007
Offline
435
#1

А зачем столько городить?

Зачем делать SELECT *, если, например, содержимое записи (TEXT) не нужен.

Зачем делать хитрые JOIN-ы, если запросы по PK выполняются в разы быстрее..

Показательно не количество запросов, а время их выполнения.

А самый быстрый запрос - тот, который не сделан.

На странице ведь тэги выводятся.. значит, полагаю, можно без запроса, уже в коде получить их ID-шники и сделать из них через запятую что-то вроде такого:

$tag_ids = implode(',',$tag_ids); // строка вида '2,3,5,7';

Далее 2 "простых" запроса по ключу:

$q1 = "SELECT post_id, count(tag_id) as cnt FROM TABLE_3 
WHERE tag_id <> $post_id AND tag_id IN ($tag_ids)
GROUP BY post_id ORDER BY 2 DESC LIMIT 0, 5";// 5 самых популярных
//... Разбираем, получаем строку вида id1,id2,id3 и аналогично запрашиваем посты
$q2 = "SELECT id, title FROM table WHERE id in ($post_ids)";

Ещё есть смысл индексы перепроверить...

... :) Облачные серверы от RegRu - промокод 3F85-3D10-806D-7224 ( http://levik.info/regru )
Станислав
На сайте с 27.12.2009
Offline
237
#2
ivan-lev:
А зачем столько городить?
Зачем делать SELECT *, если, например, содержимое записи (TEXT) не нужен.
Зачем делать хитрые JOIN-ы, если запросы по PK выполняются в разы быстрее..
Показательно не количество запросов, а время их выполнения.

Это все понятно, это не мой запрос, я даже четко его разобрать не в силах, одна каша.

Знаю только то, что он был написан в 2007 году, наверное тогда это было нормой.

ivan-lev:
А самый быстрый запрос - тот, который не сделан.
На странице ведь тэги выводятся.. значит, полагаю, можно без запроса, уже в коде получить их ID-шники и сделать из них через запятую что-то вроде такого:
$tag_ids = implode(',',$tag_ids); // строка вида '2,3,5,7';

Со страницы могу взять только ID поста, в смысле TABLE_3.id_post

А если получать ID всех тегов в посте, придется делать еще одну склейку таблиц с TABLE_2 (думаю смысла в этом нет) и быстрее наверное будет сделать отдельную выборку из TABLE_3, получив при этом все TABLE_2.id (все id тегов)

SELECT * FROM TABLE_3 WHERE id_post = 100

Тем самым получить уже все id_tag

ivan-lev:

Далее 2 "простых" запроса по ключу:

$q1 = "SELECT post_id, count(tag_id) as cnt FROM TABLE_3 

WHERE tag_id <> $post_id AND tag_id IN ($tag_ids)
GROUP BY post_id ORDER BY 2 DESC LIMIT 0, 5";// 5 самых популярных
//... Разбираем, получаем строку вида id1,id2,id3 и аналогично запрашиваем посты
$q2 = "SELECT id, title FROM table WHERE id in ($post_ids)";


Ещё есть смысл индексы перепроверить...

Побег кумекать, тестить :) спасибо!

Ну если еще варианты будут, тоже будет интересно поглядеть =)

A
На сайте с 19.07.2010
Offline
130
#3

какя-то каша в запросе.

В подзапросе линкуем TABLE3 саму на себя, TABLE1 там не нужна

как-то так подзапрос, не проверял:


(SELECT r2.post_id as id, count(*) as t_num
FROM TABLE_3 r
join TABLE_3 r2 on r.tag_id = r2.tag_id
WHERE r.post_id = '10280' and r2.post_id != '10280'
GROUP BY r2.post_id
ORDER BY count(*) DESC
LIMIT 4)

к подзапросу линкуем TABLE1 как обычно.

проверьте еще чтобы на TABLE3 было 3 индекса:

уникальный индекс: post_id, tag_id или (tag_id, post_id)

обычный индекс: tag_id

обычный индекс: post_id

.............
Станислав
На сайте с 27.12.2009
Offline
237
#4

по индексам

post_id и tag_id c INDEX идут

и плюсом

Эти два поля уникальные в данной таблицу.

IL
На сайте с 20.04.2007
Offline
435
#5
admak:
уникальный индекс: post_id, tag_id или (tag_id, post_id)
обычный индекс: tag_id
обычный индекс: post_id

post_id, tag_id - PK (полагаю)

tag_id - INDEX

а зачем отдельный индекс по post_id?

Станислав
На сайте с 27.12.2009
Offline
237
#6
admak:
какя-то каша в запросе.
TABLE1 там не нужна

Она нужно, так как нужно именно их нее выдернуть данные, в данном случае (title и id)

post_id, tag_id - PK (полагаю)

Нет. unique выставлено

В TABLE_2 и TABLE_3

данных хранятся в таком виде

TABLE_2 - теги

id | tag
1 | слово
2 | слово 2
3 | слово 3
4 | слово 4
5 | слово 5

TABLE_3

tag_id | post_id
1 | 100
2 | 100
3 | 100
4 | 100
5 | 100

Где 100 это TABLE.id = 100

IL
На сайте с 20.04.2007
Offline
435
#7
Ms-Dred:
Она нужно, так как нужно именно их нее выдернуть данные, в данном случае (title и id)

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

Станислав
На сайте с 27.12.2009
Offline
237
#8

ivan-lev, собрал запрос, вроде бы все получилось, единственно указал еще post_id != 100

А то выводил такой же пост где я и нахожусь.

Работает быстро что очень радует, время выполнения 0.003-0.008 сек

Единственно что пришлось два раза перебирать выборку, первый раз получить ID всех тегов

SELECT `tag_id`
FROM (`TABLE_3`)
WHERE `post_id` = '8722'

Получил

Array
(
[0] => stdClass Object
(
[tag_id] => 136
)

[1] => stdClass Object
(
[tag_id] => 137
)

[2] => stdClass Object
(
[tag_id] => 139
)

[3] => stdClass Object
(
[tag_id] => 232
)

[4] => stdClass Object
(
[tag_id] => 239
)

[5] => stdClass Object
(
[tag_id] => 346
)

[6] => stdClass Object
(
[tag_id] => 475
)

[7] => stdClass Object
(
[tag_id] => 809
)

[8] => stdClass Object
(
[tag_id] => 844
)

[9] => stdClass Object
(
[tag_id] => 1501
)

[10] => stdClass Object
(
[tag_id] => 3270
)

)

Далее перебор


foreach($tag_id as $v)
{
$list_id[]= $v->tag_id;
}
$list_id = implode(',', $list_id);

Затем новый запрос

SELECT post_id, count(tag_id) as cnt FROM TABLE_3 
WHERE tag_id <> 8722 AND tag_id IN ($list_id) AND post_id != 8722
GROUP BY post_id ORDER BY 2 DESC LIMIT 0, 4

Получил

Array
(
[0] => stdClass Object
(
[post_id] => 6070
[cnt] => 8
)

[1] => stdClass Object
(
[post_id] => 6120
[cnt] => 7
)

[2] => stdClass Object
(
[post_id] => 6118
[cnt] => 7
)

[3] => stdClass Object
(
[post_id] => 7110
[cnt] => 7
)

)

Далее опять перебор

foreach($list_query as $v)
{
$list_id_new[] = $v->post_id;
}

$list_id_new = implode(',', $list_id_new);

И уже последний запрос выводит то что мне нужно

SELECT id, title FROM table WHERE id in ($list_id_new)

Работает как и говорил быстро, если учесть что запрос из первого поста работал

0.1-0.3 сек

ivan-lev, о такой выборке вы писали? Или я тут уже отсебячины нагородил? 🤪

IL
На сайте с 20.04.2007
Offline
435
#9
Ms-Dred:
Далее перебор

Для получения всех значений из 1 колонки удобно использовать fetchAll с FETCH_COLUMN

fetchAll(PDO::FETCH_COLUMN, 0);

Суть не меняется, зато код компактнее и понятнее.

Станислав
На сайте с 27.12.2009
Offline
237
#10

Без вас бы не справился =) Спасибо!

12

Авторизуйтесь или зарегистрируйтесь, чтобы оставить комментарий