У меня очень сложный запрос, который использует некоторые подзапросы в операторе CASE.Как предотвратить зависимые подзапросы в CASE КОГДА x (подзапрос)
Для этого вопроса полный запрос не нужен и просто не позволит людям быстро попасть в проблему.
Так что этот пост использует псевдокод для работы. Если захочу, я мог бы отправить запрос, но это монстр и не нужен для этого вопроса.
Что я хочу, это кэшируемые подзапросы в операторе CASE.
SELECT * FROM posts posts
INNER JOIN posts_shared_to shared_to
ON shared_to.post_id = posts.post_id
INNER JOIN channels.channels
ON channels.channel_id = shared_to.channel_id
WHERE posts.parent_id IS NULL
AND MATCH (post.text) AGAINST (:keyword IN BOOLEAN MODE)
AND CASE(
WHEN channel.read_access IS NULL THEN 1
WHEN channel.read_access = 1 THEN
(
SELECT count(*) FROM channel_users
WHERE user_id = XXX AND channel_id = channels.channel_id
)
WHEN shared_to.read_type = 2 THEN
(
/* another subquery with a join */
/* check if user is in friendlist of post_author */
)
ELSE 0
END;
)
GROUP BY post.post_id
ORDER BY post.post_id
DESC LIMIT n,n
Как статут над этим является просто упрощенным псевдокодом.
MySql EXPLAIN говорит, что все используемые подзапросы в CASE являются DEPENDENT, что означает (если я прав), что они должны запускаться каждый раз и не кэшироваться.
Любое решение, которое помогает ускорить этот запрос, приветствуется.
EDITED ЧАСТЬ: Теперь истинный запрос выглядит следующим образом:
SELECT a.id, a.title, a.message AS post_text, a.type, a.date, a.author AS uid,
b.a_name as name, b.avatar,
shared_to.to_circle AS circle_id, shared_to.root_circle,
c.circle_name, c.read_access, c.owner_uid, c.profile,
MATCH(a.title,a.message) AGAINST (:keyword IN BOOLEAN MODE) AS score
FROM posts a
/** get userdetails for post_author **/
JOIN authors b ON b.id = a.author
/** get circles posts was shared to **/
JOIN posts_shared_to shared_to ON shared_to.post_id = a.id AND shared_to.deleted IS NULL
/**
* get circle_details note: at the moment shared_to can contain NULL and 1 too and doesnt need to be a circle_id
* if to_circle IS NULL post was shared public
* if to_circle = 1 post was shared to private circles
* since we use md5 keys as circle ids this can be a string insetad of (int) ... ugly..
*
**/
LEFT JOIN circles c ON c.circle_id = shared_to.to_circle
/*AND c.circle_name IS NOT NULL */
AND (c.profile IS NULL OR c.profile = 6 OR c.profile = 1)
AND c.deleted IS NULL
LEFT JOIN (
/** if post is within a channel that requires membership we use this to check if requesting user is member **/
SELECT COUNT(*) users_count, user_id, circle_id FROM circles_users
GROUP BY user_id, circle_id
) counts ON counts.circle_id = shared_to.to_circle
AND counts.user_id = :me
LEFT JOIN (
/** if post is shared private we check if requesting users exists within post authors private circles **/
SELECT count(*) in_circles_count, ci.owner_uid AS circle_owner, cu1.user_id AS user_me
FROM circles ci
INNER JOIN circles_users cu1 ON cu1.circle_id = ci.circle_id
AND cu1.deleted IS NULL
WHERE ci.profile IS NULL AND ci.deleted IS NULL
GROUP BY user_me, circle_owner
) users_in_circles ON users_in_circles.user_me = :me
AND users_in_circles.circle_owner = a.id
/** make sure post is a topic **/
WHERE a.parent_id IS NULL AND a.deleted IS NULL
/** search title and post body **/
AND MATCH (a.title,a.message) AGAINST (:keyword IN BOOLEAN MODE)
AND (
/** own circle **/
c.owner_uid = :me
/** site member read_access (this query is for members, for guests we use a different query) **/
OR (c.read_access = 1 OR c.read_access = "1")
/** public read_access **/
OR (shared_to.to_circle IS NULL OR (c.read_access IS NULL AND c.owner_uid IS NOT NULL))
/** channel/circle member read_access**/
OR (c.read_access = 3 OR c.read_access = "3" AND counts.users_count > 0)
/** for users within post creators private circles **/
OR (
(
/** use shared_to to determine if post is private **/
shared_to.to_circle = "1" OR shared_to.to_circle = 1
/** use circle settings to determine global privacy **/
OR (c.owner_uid IS NOT NULL AND c.read_access = 2 OR c.read_access = "2")
) AND users_in_circles.circle_owner = a.author AND users_in_circles.user_me = :me
)
)
GROUP BY a.id ORDER BY a.id DESC LIMIT n,n
Вопрос: ли это на самом деле лучше? Если я посмотрю, сколько строк могут содержать производные таблицы, я не уверен в этом.
А может быть, кто-то может помочь мне изменения запроса, как упомянуто @ Олли-Джонс:
SELECT stuff, stuff, stuff
FROM (
SELECT post.post_id
FROM your whole query
ORDER BY post_id DESC
LIMIT n,n
) ids
JOIN whatever ON whatever.post_id = ids.post_id
JOIN whatelse ON whatelse
SRY, если этот звук slazy, но я на самом деле не mysqlguy и у меня головные боли в течение многих лет только от строительства этот запрос. : D
Хотел бы я утверждать, что запросы в моем ответе были отладки и готовы к использованию. Но я не такой уж большой лжец. –
Улыбка. Выглядит гениально. Благодарю. Дает мне достаточно новых идей для работы. – user2429266
Хорошо. Я глубоко вздохнул и работал через ваш ввод. (Трудно создать исходный запрос таким образом. Было сложно построить его вообще.) Является ли независимый подзапрос действительно более эффективным, если они работают на больших таблицах? – user2429266