MySQL Group By with multi-join - mysql

here is my SQL query:
`SELECT subject, threadpost.date, threadpost.idThreadPost as id,
threadcategories.category, users.userName, COUNT(idThreadSubs) AS subs, COUNT(idThreadReplies) as replies
FROM threadpost
JOIN threadcategories
ON idthreadcategories = threadpost.category
JOIN users
ON idUsers = UserId
LEFT JOIN threadsubs
ON threadpost.idThreadPost = threadsubs.ThreadId
LEFT JOIN threadreplies
ON threadpost.idThreadPost = threadreplies.ThreadId
WHERE idthreadcategories LIKE ?
GROUP BY idThreadPost
ORDER BY date desc
LIMIT 20;`
the problem comes with adding COUNT(idThreadReplies). As you can see, I'm grouping by idThreadPost. This is because I want to retrieve both the count of subscriptions to the thread, and the count of replies.
However, the result gives me the incorrect number of replies (the same number as subscriptions).
How would I formulate this query correctly?

figured it out. solution is to use subqueries in the joins that require group by:
`SELECT subject, threadpost.date, threadpost.idThreadPost as id,
threadcategories.category, users.userName, tsubs.subs AS subs, trep.replies as replies
FROM threadpost
JOIN threadcategories
ON idthreadcategories = threadpost.category
JOIN users
ON idUsers = UserId
LEFT JOIN (
SELECT threadsubs.ThreadId as tsubId, COUNT(idThreadSubs) as subs
FROM threadsubs
GROUP BY idThreadSubs
) as tsubs
ON tsubs.tsubId = threadpost.idThreadPost
LEFT JOIN (
SELECT threadreplies.ThreadId as tId, COUNT(threadreplies.idThreadReplies) as replies
FROM threadreplies
GROUP BY threadreplies.ThreadId
) AS trep
ON trep.tId = threadpost.idThreadPost
WHERE idthreadcategories LIKE ?
ORDER BY date desc
LIMIT 20;`

Related

SQL query that limits the results to one when using count inside count

I am trying to select the count of likes on a specific project. The idea i came up with is
CAST(count(uploads.ID in (SELECT uploadID from votes)) as decimal) as numberoflikes
this works but the query then only returns one thing.
Entire query
SELECT DISTINCT users.NAME AS username
,users.ID AS userID
,subjects.NAME AS subjectname
,uploads.TIME
,uploads.description
,uploads.NAME
,uploads.ID
,CASE
WHEN uploads.ID IN (
SELECT uploadID
FROM votes
WHERE userID = 2
)
THEN CAST(1 AS DECIMAL)
ELSE CAST(0 AS DECIMAL)
END AS liked
,CASE
WHEN uploads.ID IN (
SELECT uploadID
FROM bookmarks
WHERE userID = 2
)
THEN CAST(1 AS DECIMAL)
ELSE CAST(0 AS DECIMAL)
END AS bookmarked
,CAST(count(uploads.ID IN (
SELECT uploadID
FROM votes
)) AS DECIMAL) AS numberoflikes
FROM uploads
INNER JOIN subjects ON (subjects.ID = uploads.subjectID)
INNER JOIN users ON (users.ID = uploads.userID)
INNER JOIN uploadGrades ON (uploads.ID = uploadGrades.uploadID)
INNER JOIN grades ON (grades.ID = uploadGrades.gradeID)
WHERE uploads.active = 1
AND subjects.ID IN (
SELECT subjectID
FROM userSubjects
INNER JOIN users ON (users.ID = userSubjects.userID)
WHERE userSubjects.userID = 2
)
AND grades.ID IN (
SELECT userGrades.gradeID
FROM uploadGrades
INNER JOIN userGrades ON (uploadGrades.gradeID = userGrades.gradeID)
WHERE userGrades.userID = 2
)
ORDER BY uploads.trueRating DESC;
Lets try a reduce version of your query, That is the base to get better answers
I reduce the initial query to user and upload to start. Also remove the fields you already know how to calculate.
.
SELECT DISTINCT users.NAME AS username
,users.ID AS userID
,uploads.NAME
,uploads.ID
,CAST(count(uploads.ID IN (
SELECT uploadID
FROM votes
)) AS DECIMAL) AS numberoflikes
FROM uploads
INNER JOIN users ON (users.ID = uploads.userID)
WHERE uploads.active = 1
ORDER BY uploads.trueRating DESC;
Then add votes with LEFT JOIN to replace the SELECT in the COUNT that way if not match you will get NULL and as I say in my comment COUNT doesnt count NULL's
.
SELECT DISTINCT users.NAME AS username
,users.ID AS userID
,uploads.NAME
,uploads.ID
,CAST(count(votes.uploadID)) AS DECIMAL) AS numberoflikes
FROM uploads
INNER JOIN users ON (users.ID = uploads.userID)
LEFT JOIN votes ON (uploads.ID = votes.uploadID)
WHERE uploads.active = 1
ORDER BY uploads.trueRating DESC;
Try something like this...
SELECT users.name as username, users.ID as userID, subjects.name as subjectname,
uploads.time, uploads.description, uploads.name, uploads.ID,
count(userVotes.userId), count(bookmarksMade.userId),
FROM uploads
join subjects on(subjects.ID = uploads.subjectID)
join users on(users.ID = uploads.userID)
join uploadGrades on(uploads.ID = uploadGrades.uploadID)
join grades on(grades.ID = uploadGrades.gradeID)
left join (select userId, uploadId from votes where userId = 2) as userVotes on uploads.id = userVotes.uploadId
left join (select userId, uploadId from bookmarks where userId = 2) as bookmarksMade on uploads.id = bookmarksMade.uploadId
join userSubjects on subjects.id = userSubjects.subjectID
WHERE uploads.active = 1 AND
userSubjects.userID = 2
ORDER BY uploads.trueRating DESC;
But, I am leaving out the userGrades thing, because you are doing a funky join there that I don't really understand (joining two tables on something that looks like it is not the whole primary key on either table).
Anyway, you really need to go to something more like this or what Oropeza suggests in his answer. Get more direct about what you want. This query looks like a monster that has been growing and getting things added in with "IN" clauses, as you needed them. Time to go back to the drawing board and think about what you want and how to get at it directly.
count(uploads.ID in (SELECT uploadID from votes)) as numberoflikes
group by uploads.Id ORDER BY uploads.trueRating DESC
I managed to do it like this. If i added the group by then it split the numberoflikes into rows and returned more then one row. Thanks for the help!

MySQL Query, multiple counts and sums

I have a MySQL query that outputs to a php table but I'm having issues in joining two tables that both use a COUNT:
$query = "SELECT mqe.registration,
COUNT(*) AS numberofenqs,
COUNT(DISTINCT ucv.ip) AS unique_views,
SUM(ucv.views) AS total_views
FROM main_quick_enquiries AS mqe
LEFT OUTER JOIN used_car_views AS ucv
ON ucv.numberplate = mqe.registration
WHERE mqe.registration IS NOT NULL
GROUP BY mqe.registration ORDER BY numberofenqs DESC";
The query runs, but the number within the numberofenqs column is always wrong as i know from performing that query on its own that it comes in with the correct result:
SELECT registration, COUNT(*) AS numberofenqs FROM main_quick_enquiries GROUP BY registration ORDER BY numberofenqs DESC
Why is the COUNT(*) not working correctly in top query code and where is it getting the figures from?
it could be because of LEFT OUTER JOIN ...
Try to run this:
SELECT registration
, count(*)
FROM main_quick_enquiries
GROUP BY registration
and compare it with this result
SELECT mqe.registration
, count(*)
FROM main_quick_enquiries mqe
LEFT OUTER JOIN used_car_views ucv
ON ucv.numberplate = mqe.registration
GROUP BY mqe.registration
There could be a problem :) in duplicity rows... try to find one specific registration number, and compare the details of both query
SELECT *
FROM main_quick_enquiries
WHERE registration = XXXX
+
SELECT *
FROM main_quick_enquiries mqe
LEFT OUTER JOIN used_car_views ucv
ON ucv.numberplate = mqe.registration
WHERE registration = XXXX
you should see the diffs
Thanks All, but I think I've nailed it with COUNT(DISTINCT mqe.id) instead of COUNT(*).

sql counts wrong number of likes

I have written an sql statement that besides all the other columns should return the number of comments and the number of likes of a certain post. It works perfectly when I don't try to get the number of times it has been shared too. When I try to get the number of time it was shared instead it returns a wrong number of like that seems to be either the number of shares and likes or something like that. Here is the code:
SELECT
[...],
count(CS.commentId) as shares,
count(CL.commentId) as numberOfLikes
FROM
(SELECT *
FROM accountSpecifics
WHERE institutionId= '{$keyword['id']}') `AS`
INNER JOIN
account A ON A.id = `AS`.accountId
INNER JOIN
comment C ON C.accountId = A.id
LEFT JOIN
commentLikes CL ON C.commentId = CL.commentId
LEFT JOIN
commentShares CS ON C.commentId = CS.commentId
GROUP BY
C.time
ORDER BY
year, month, hour, month
Could you also tell me if you think this is an efficient SQL statement or if you would do it differently? thank you!
Do this instead:
SELECT
[...],
(select count(*) from commentLikes CL where C.commentId = CL.commentId) as shares,
(select count(*) from commentShares CS where C.commentId = CS.commentId) as numberOfLikes
FROM
(SELECT *
FROM accountSpecifics
WHERE institutionId= '{$keyword['id']}') `AS`
INNER JOIN account A ON A.id = `AS`.accountId
INNER JOIN comment C ON C.accountId = A.id
GROUP BY C.time
ORDER BY year, month, hour, month
If you use JOINs, you're getting back one result set, and COUNT(any field) simply counts the rows and will always compute the same thing, and in this case the wrong thing. Subqueries are what you need here. Good luck!
EDIT: as posted below, count(distinct something) can also work, but it's making the database do more work than necessary for the answer you want to end up with.
Quick fix:
SELECT
[...],
count(DISTINCT CS.commentId) as shares,
count(DISTINCT CL.commentId) as numberOfLikes
Better approach:
SELECT [...]
, Coalesce(shares.numberOfShares, 0) As numberOfShares
, Coalesce(likes.numberOfLikes , 0) As numberOfLikes
FROM [...]
LEFT
JOIN (
SELECT commentId
, Count(*) As numberOfShares
FROM commentShares
GROUP
BY commentId
) As shares
ON shares.commentId = c.commentId
LEFT
JOIN (
SELECT commentId
, Count(*) As numberOfLikes
FROM commentLikes
GROUP
BY commentId
) As likes
ON likes.commentId = c.commentId

Selecting from two tables and the latest entry

I keep running on an issue in calling from MySQL and getting what I need. I have two tables called projects and entries, what I am trying to do is get the latest time stamped entry. The SQL query is as follows:
SELECT
projects.ProjectLogo, projects.ProjectLink, projects.ProjectDescription,
entries.EntryNum, entries.Votes, entries.Views, entries.Update
FROM
projects
LEFT JOIN entries
ON projects.ProjectID = entries.ProjectID
AND projects.Media = 'image'
AND projects.Type = 'fan-art'
GROUP BY
projects.ProjectID
ORDER BY
entries.Update DESC
The issue is that I get the results but not the latest entry, I have used MAX(entries.Update) but it does not work. Any suggestions? Why does it not work?
You can use a subquery to get the latest Update for every ProjectID on table entries. The result of the subquery is then join back on the two join statements providied that it will match on two columns: ProjectID and Update.
SELECT projects.ProjectLogo,
projects.ProjectLink,
projects.ProjectDescription,
entries.EntryNum,
entries.Votes,
entries.Views,
entries.Update
FROM projects
INNER JOIN entries
ON projects.ProjectID = entries.ProjectID
INNER JOIN
(
SELECT a.ProjectID, MAX(a.Update) max_val
FROM entries a
GROUP BY a.ProjectID
) b ON b.ProjectID = entries.ProjectID AND
b.max_val = entries.Update
WHERE projects.Media = 'image' AND
projects.Type = 'fan-art'
ORDER BY entries.Update DESC
Try using a subquery to get the latest entry:
select projectId, max(`update`) as lastUpdate
from entries
group by projectId
And now, use this subquery to get what you need:
select ...
from
projects as p
inner join entries as e on p.projectId=e.projectId
inner join (
select projectId, max(`update`) as lastUpdate
from entries
group by projectId) as me on e.`update`=me.lastUpdate
Hope this helps you
You have to restrict the join to only the record (row) with the latest entry date.
if the pk in entries is chronologically increasing, you can use it.
Select p.ProjectLogo, p.ProjectLink, p.ProjectDescription,
e.EntryNum, e.Votes, e.Views, e.Update
From projects p
Left Join entries e
On e.EntryId =
(Select(Max(entryId) from entries
where ProjectID = p.ProjectID)
Where p.Media = 'image'
And p.Type = 'fan-art'
Group By p.ProjectID
Order By e.Update Desc
Otherwise, you need a double nested subquery
Select p.ProjectLogo, p.ProjectLink, p.ProjectDescription,
e.EntryNum, e.Votes, e.Views, e.Update
From projects p
Left Join entries e
On e.EntryId =
(Select entryId from entries
where ProjectID = p.ProjectID
And update =
(Select max(update) From entries
Where ProjectID = p.ProjectID))
where p.Media = 'image'
And p.Type = 'fan-art'
Group By p.ProjectID
Order By e.Update Desc

MySQL Query Help, including a count?

Any ideas why this isn't working in MySQL?
SELECT blogentry.*,
person.personName,
(SELECT *
FROM BlogEntryComment
Where BlogEntryID = '8') as CommentCount
FROM blogentry
INNER JOIN person ON blogentry.personID = person.personID
WHERE blogentry.deleted = 'N'
ORDER BY blogentry.dateAdded DESC
The subquery needs to return only one value: the field count. * returns all rows, whereas count(*) will return how many there are.
(SELECT count(*) FROM BlogEntryComment Where BlogEntryID = '8')
You have to use the aggregate function COUNT in order to get the value - SELECT * in a subSELECT will frail, because it is attempting to return all the column values for a row into a single column.
That said, what you have will return the same CommentCount value for every BLOGENTRY record returned. The following is a better approach:
SELECT be.*,
p.personname,
COALESCE(x.num, 0) AS CommentCount
FROM BLOGENTRY be
JOIN PERSON p ON p.personid = be.personid
LEFT JOIN (SELECT bec.blogentryid,
COUNT(*) AS num
FROM BLOGENTRYCOMMENT bec
GROUP BY bec.blogentryid) x ON x.blogentryid = be.blogentryid