Can anyone help me tame this unwieldy MySQL query before it gets worse?
I'm not very good at SQL, but I'm certainly learning.
So this one provides content for a couple of lists below wordpress posts. One is, "More popular articles from this user," and the other is "More popular articles in this category."
Articles can be published by editors, or submitted by logged in or anonymous users. It just checks all the articles to see whether they're from the same user, or under the same category as the post the list is being displayed for. When a user submits an article, it's still "posted by" whichever admin posted the article (since they must be approved first). So there's a separate db entry which is present when a user submits an article.
The basic structure:
select post_id, post_title, post_name, (
//number of times this article has been
//favorited, to help sort them
) as favorited, (
//whether this article is from the same user
select count(*) > 0 from wp_users //have to put something, it's just a flag
where exists (
//see if this post was authored by the the same开发者_StackOverflow社区 user(s)
and not exists (
//make sure it's not a user submitted article. If it is,
//we want to group it by the submitting user, not the
//approving editor
)
)
or exists (
//see if an 'original submitter username' exists for this post,
//and if so, see if it matches the post we're generating the list for
)
) as under_users, (
//see if this post is under the same category
) as under_category
And my messy query:
select p.ID, p.post_title, p.post_name, (
select count(*)
from wp_usermeta as um
where um.meta_key = 'wpfp_favorites'
and POSITION(CONCAT("\"",p.ID,"\"") IN um.meta_value)
) as favorited, (
select count(*) > 0 from wp_users
where exists (
select *
from wp_terms as t, wp_term_taxonomy as tt, wp_term_relationships as tr
where tt.taxonomy = 'author'
and tr.object_id = p.ID
and tr.term_taxonomy_id = tt.term_taxonomy_id
and t.term_id = tt.term_id
and t.term_id in (
select t2.term_id
from wp_terms as t2, wp_term_taxonomy as tt2, wp_term_relationships as tr2
where tt2.taxonomy = 'author'
and tr2.object_id = 535
and tr2.term_taxonomy_id = tt2.term_taxonomy_id
and t2.term_id = tt2.term_id
)
and not exists (
select *
from wp_postmeta as pm
where pm.post_id = 535
and pm.meta_key = 'Original Submitter Username'
)
)
or exists (
select *
from wp_postmeta as pm
where pm.post_id = p.ID
and pm.meta_key = 'Original Submitter Username'
and pm.meta_value = (
select pm2.meta_value
from wp_postmeta as pm2
where pm2.post_id = 535
and pm2.meta_key = 'Original Submitter Username'
)
)
) as under_users, (
select count(*) > 0 from wp_users
where exists (
select *
from wp_terms as t, wp_term_taxonomy as tt, wp_term_relationships as tr
where tt.taxonomy = 'category'
and tr.object_id = p.ID
and tr.term_taxonomy_id = tt.term_taxonomy_id
and t.term_id = tt.term_id
and t.term_id in (
select t2.term_id
from wp_terms as t2, wp_term_taxonomy as tt2, wp_term_relationships as tr2
where tt2.taxonomy = 'category'
and tr2.object_id = 535
and tr2.term_taxonomy_id = tt2.term_taxonomy_id
and t2.term_id = tt2.term_id
and t2.term_id not in (3, 4)
)
)
) as under_category
from wp_posts as p
where p.post_type = 'post'
and p.ID != 535
and p.post_status = 'publish'
having (
under_users != 0
or under_category != 0
)
order by favorited desc
I feel like it could be so much shorter, and better, but I don't know how to do it. I seem to be querying for the same things several times in the query and, I'm afraid to add anything else to it (differentiate between logged-in and anon submitters, sort articles by views as well as favorites, etc) lest it collapse in on itself and become a black hole.
Any tips to help me with this?
You might be better off splitting off some of these subqueries at the application level. You can then change your EXISTS
clauses to EXISTS IN (ids...)
.
I've found that nested subqueries in MySQL tend to be horribly slow because of the number of rows that need to be examined at once. Breaking the subqueries on the application level allows you to employ caching, and gives you greater control of what your subqueries are doing, all while making your SQL easier to read.
Don't worry too much about your query being complicated. In real-world applications queries get like this. If it starts to become a problem (remember you can embed comments inside an SQL statement), you can create views that take care of some of the subqueries. For example your subquery for under_category. You could create a view thus...
create view under_category_view as
select tr.object_id AS p_id
from wp_terms as t,
wp_term_taxonomy as tt,
wp_term_relationships as tr
where tt.taxonomy = 'category'
and tr.term_taxonomy_id = tt.term_taxonomy_id
and t.term_id = tt.term_id
and t.term_id in (select t2.term_id
from wp_terms as t2,
wp_term_taxonomy as tt2,
wp_term_relationships as tr2
where tt2.taxonomy = 'category'
and tr2.object_id = 535
and tr2.term_taxonomy_id = tt2.term_taxonomy_id
and t2.term_id = tt2.term_id
and t2.term_id not in (3, 4));
and then in your big query you would use...
select count(*) > 0 from wp_users
where exists (select *
from user_category_view
where p_id = p.id) as under_category
Incidentally, I find splitting lines vertically and using big indents as I have done here helps to make a chunky query easier to read.
精彩评论