Reputation: 5877
This query is a bit slow, and I'd like to optimize it, any ideas?
SELECT DISTINCT a.id
FROM article a
LEFT JOIN article_comment ac
ON a.id = ac.article_id
LEFT JOIN comment c
ON ac.id = c.id
WHERE a.id IN (SELECT a2.id
FROM article_user_read aur
LEFT JOIN article a2
ON aur.article_id = a2.id
WHERE c.published_date > aur.read_date
AND aur.user_id = 36748
AND aur.followed = 1)
ORDER BY c.published_date DESC
here is the article_user_read table :
CREATE TABLE `article_user_read` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`article_id` int(11) DEFAULT NULL,
`user_id` int(11) DEFAULT NULL,
`read_date` datetime NOT NULL,
`followed` tinyint(1) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `UNIQ_BBE52A0262922701A76ED395` (`article_id`,`user_id`),
KEY `IDX_BBE52A0262922701` (`article_id`),
KEY `IDX_BBE52A02A76ED395` (`user_id`),
CONSTRAINT `article_user_read_ibfk_3` FOREIGN KEY (`article_id`) REFERENCES `article` (`id`),
CONSTRAINT `article_user_read_ibfk_4` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=20193 DEFAULT CHARSET=latin1;
The other are just simple article & user table.
Upvotes: 0
Views: 94
Reputation: 48139
Since you are only concerned with distinct IDs, I would pre-query just from the article_user_read table since that is the whole basis of your WHERE EXISTS, then link to articles and pull whatever other columns you want... Additionally, you were doing LEFT JOINs, but ultimately requiring a find based on your respective WHERE clause of the "c.published_date" which means all entries are required all the way down to the comments table. So LEFT JOIN is not applicable... the only other thing I would do is add an index to the article_user_read table on BOTH the User ID AND the Followed flag.
SELECT distinct
aur.article_id
from
article_user_read aur
join article a
on aur.article_id = a.id
join article_comment ac
on aur.article_id = ac.article_id
join comment c
on ac.id = c.id
and c.published_date > aur.read_date
where
aur.user_id = 36748
and aur.followed = 1
Upvotes: 0
Reputation: 3184
SELECT DISTINCT a.id
FROM article AS a
JOIN article_user_read AS aur
ON aur.article_id = a.id
JOIN comment AS c
ON ac.id = c.id
JOIN article_comment AS ac
ON a.id = ac.article_id
WHERE c.published_date > aur.read_date
AND aur.user_id = 36748
AND aur.followed = 1
ORDER BY c.published_date DESC
It doesn't make sense to LEFT JOIN
article_user_read
with article
, because you're interested in articles, so you don't care if there are articles without a article_user_read relation -- that can be optimized to a JOIN
.
You also only want articles which have a comment after the user reads the article, so the relation between article
, article_comment
and comment
must exist -- that can be optimized to JOIN
s.
The main change however, is that you don't need a correlated sub-query (referencing comment c
in the sub-query), so the JOIN
between article_user_read
and article
can be pushed to the main query.
Upvotes: 1
Reputation:
Given that you are only interested in the article IDs in this query, it can be significantly simplified to:
SELECT a.id
FROM article a
WHERE EXISTS
(SELECT NULL
FROM article_comment ac
INNER JOIN comment c ON ac.id = c.id
INNER JOIN article_user_read aur
ON aur.article_id = a.id AND c.published_date > aur.read_date
WHERE a.id = ac.article_id AND
aur.user_id = 36748 AND
aur.followed = 1)
Upvotes: 0
Reputation: 44250
A wild guess: replacing "IN" by "WHERE EXISTS" often pays off:
SELECT DISTINCT a.id
FROM article a
LEFT JOIN article_comment ac
ON a.id = ac.article_id
LEFT JOIN comment c
ON ac.id = c.id
WHERE EXISTS (
SELECT *
FROM article_user_read aur
LEFT JOIN article a2
ON aur.article_id = a2.id
WHERE c.published_date > aur.read_date
AND aur.user_id = 36748
AND aur.followed = 1
AND a.id = a2.id
)
ORDER BY c.published_date DESC
Upvotes: 0