Reputation: 3276
I have a feed
table, feedComment
table and feedLike
table.
I need, in one query, select all feeds from the database with the number of comments and likes.
This is what I have so far:
SELECT
`feed`.`id` AS `feedID`,
`feed`.`companyID`,
`feed`.`eventID`,
`feed`.`memberID` AS `personID`,
`feed`.`title`,
`feed`.`text`,
`feed`.`shares`,
UNIX_TIMESTAMP(`feed`.`date`) AS `date`,
COUNT(`feedComment`.`id`) AS `comments`
FROM `feed`
LEFT JOIN `feedComment`
ON `feedComment`.`feedID` = `feed`.`id`
GROUP BY `feed`.`id`
This works with feedComment
and feedLike
apart. If I try to use both of them, it doesn't work.
How do I accomplish that?
Upvotes: 1
Views: 62
Reputation: 108410
Given what you have so far, you could add an inline view to return the count from feedLike
.
SELECT `feed`.`id` AS `feedID`,
`feed`.`companyID`,
`feed`.`eventID`,
`feed`.`memberID` AS `personID`,
`feed`.`title`,
`feed`.`text`,
`feed`.`shares`,
UNIX_TIMESTAMP(`feed`.`date`) AS `date`,
IFNULL(`countLike`.`cnt`,0) AS `likes`,
COUNT(`feedComment`.`id`) AS `comments`
FROM `feed`
LEFT
JOIN `feedComment`
ON `feedComment`.`feedID` = `feed`.`id`
-- outer join to inline view (MySQL derived table)
LEFT
JOIN (
SELECT `feedLike`.`feedID`
, COUNT(`feedLike`.`id`) AS `cnt`
FROM `feedLike`
GROUP BY `feedLike`.`feedID`
) `countLike`
ON `countLike`.`feedID` = `feed`.`id`
GROUP BY `feed`.`id`
The query between the parens will be executed, and the results will be what MySQL calls a "derived table". We assign that derived table a name... countLike
. To the outer query, it's just like countLike
was an actual table.
The IFNULL()
function is used in the outer select to replace a possible NULL value with a zero. It's not mandatory we do this, but usually folks like to get a "zero" returned as a count, rather than a NULL.
Note that there can be significant performance issues with derived tables. (The latest version of MySQL addresses some of those problems, such as the absence of indexes on the derived table.)
This same approach can be applied to getting the count from the feedComment
table as well. Especially if there's no other need to group by feed.id
.
SELECT feed.id AS `feedID`
, feed.companyID
, feed.eventID
, feed.memberID AS `personID`
, feed.title
, feed.text
, feed.shares
, UNIX_TIMESTAMP(feed.date) AS `date`
, IFNULL(countLike.cnt,0) AS `likes`
, IFNULL(countComment.cnt,0) AS `comments`
FROM feed
LEFT
JOIN ( SELECT c.feedID
, COUNT(1) AS cnt
FROM feedComment c
GROUP BY c.feedID
) countComment
ON countComment.feedID = feed.id
LEFT
JOIN ( SELECT l.feedID
, COUNT(1) AS cnt
FROM feedLike l
GROUP BY l.feedID
) countLike
ON countLike.feedID = feed.id
DISTINCT
As an entirely different approach, you could have a query generate the partial cross-product (every row from FeedLike
for a given feedID matched to every row from FeedComment
), and then count "distinct" id
values. But this approach has the potential to generate a fairly sizable (and performance killing) intermediate result. (If a given feed
has 30 rows feedLike
and 30 rows in feedComment
, that's a total of 1 x 30 x 30 = 900 rows for a single feed.
I purposefully leave this example incomplete, to discourage this as a general practice. But I include it because it does demonstrate another approach:
SELECT feed.id
, ...
, COUNT(DISTINCT feedLike.ID)
, COUNT(DISTINCT feedComment.ID)
FROM feed
LEFT
JOIN feedLike
ON ...
LEFT
JOIN feedComment
ON ...
GROUP BY feed.id
Upvotes: 1
Reputation: 439
Why not use subquerys? (Works on Sql Server at least)
Select `feed`.`id` AS `feedID`,
`feed`.`companyID`,
`feed`.`eventID`,
`feed`.`memberID` AS `personID`,
`feed`.`title`,
`feed`.`text`,
`feed`.`shares`,
UNIX_TIMESTAMP(`feed`.`date`) AS `date`,
(Select count(`feedComment`.`id`) from feedComment Where commentid = F.id) as 'Comment',
(Select count('feedLike'.'id') from feedLike Where likeid = F.id) as 'Something else'
From feed F
If you just need the count I dont see why use join
Upvotes: 1