Ich habe eine sehr komplexe Abfrage, die einige Unterabfragen innerhalb einer CASE-Anweisung verwendet.Verhindern abhängiger Unterabfragen in CASE WHEN x THE (Unterabfrage)
Für diese Frage ist die vollständige Abfrage nicht erforderlich und würde nur verhindern, dass Menschen schnell in das Problem eindringen.
Also dieser Beitrag verwendet Pseudocode mit zu arbeiten. Wenn gewünscht, könnte ich die Abfrage posten, aber es ist ein Monster und nutzlos für diese Frage.
Was ich will sind cachefähige Unterabfragen innerhalb der CASE-Anweisung.
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
Wie oben erwähnt, ist dies nur ein vereinfachter Pseudocode.
MySql EXPLAIN sagt, dass alle verwendeten Unterabfragen innerhalb des CASE ABHÄNGIG sind, was bedeutet (wenn ich richtig bin), dass sie jedes Mal ausgeführt werden müssen und nicht zwischengespeichert werden.
Jede Lösung, die zur Beschleunigung dieser Abfrage beiträgt, ist willkommen.
TEIL EDITED: Nun ist die wahre Abfrage wie folgt aussieht:
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
Frage: Ist das wirklich der bessere Weg? Wenn ich mir anschaue, wie viele Zeilen die abgeleiteten Tabellen enthalten können, bin ich mir nicht sicher.
Und vielleicht kann mir jemand helfen, durch @ Ollie-Jones die Abfrage wie erwähnt zu ändern:
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 wenn dieser Ton slazy aber ich nicht wirklich ein mysqlguy bin und ich bekam Kopfschmerzen seit Jahren nur vom Bau diese Abfrage. : D
Ich wünschte, ich könnte behaupten, dass die Abfragen in meiner Antwort debuggte und einsatzbereit waren. Aber ich bin kein großer Lügner. –
Lächeln. Sieht genial aus. Vielen Dank. Gibt mir genug neue Ideen, mit denen ich arbeiten kann. – user2429266
Ok. Ich nahm einen tiefen Atemzug und arbeitete durch deine gegebene Eingabe. (Es ist schwierig, die ursprüngliche Abfrage auf diese Weise zu erstellen. Es war schwierig, sie überhaupt zu erstellen.) Ist die unabhängige Unterabfrage wirklich effektiver, wenn sie auf großen Tabellen ausgeführt wird? – user2429266