Grouping issue with SELECT query - mysql

I am building a very basic level Event management & Invitation system. For that I have build 2 tables and using a query to list all events with their Invitation counts as
1. Total Invitations Sent
2. Total Accepted
3. Total Rejected
4. Total Waiting
Below is the query
SELECT
*,
count(im.event_id_fk) as total_invitations,
count(im2.event_id_fk) as total_accepted,
count(im3.event_id_fk) as total_rejected,
count(im4.event_id_fk) as total_waiting
FROM event_mst em
LEFT JOIN invitation_mst im
ON (em.event_id_pk = im.event_id_fk)
LEFT JOIN invitation_mst im2
ON (em.event_id_pk = im2.event_id_fk AND im2.status = 'Accept')
LEFT JOIN invitation_mst im3
ON (em.event_id_pk = im3.event_id_fk AND im3.status = 'Reject')
LEFT JOIN invitation_mst im4
ON (em.event_id_pk = im4.event_id_fk AND im4.status = 'Waiting')
GROUP BY
im.event_id_fk,
im2.event_id_fk,
im3.event_id_fk,
im4.event_id_fk
ORDER BY
em.date_added DESC
Now the issue is that this query is giving Wrong counts, e.g. if there are total 3 Invitations sent, it is giving 9. If there are 5 invitations sent, it is giving 25.
so it seems that it is multiplying with itself and returning the result. I know there must be something wrong with this select query. Anyone correct me this query ?
Thanks in advance.

This is the simpliest solution for mysql because it supports boolean calculation.
SELECT event_id_pk,
COUNT(*) as total_invitations,
SUM(status = 'Accept') as total_accepted,
SUM(status = 'Reject') as total_rejected,
SUM(status = 'Waiting') as total_waiting
FROM event_mst
GROUP BY event_id_pk
but the query will not give you full details on the invitation, in order to do that, you can wrap the query in the subquery and join that with the original table itself,
SELECT a.*,
b.total_invitations,
b.total_rejected,
b.total_waiting
FROM event_mst a
INNER JOIN
(
SELECT event_id_pk,
COUNT(*) as total_invitations,
SUM(status = 'Accept') as total_accepted,
SUM(status = 'Reject') as total_rejected,
SUM(status = 'Waiting') as total_waiting
FROM event_mst
GROUP BY event_id_pk
) b ON a.event_id_pk = b.event_id_pk

Related

mysql count for same column but two counts

I am trying to do a count on the same column having different status, how can i do it, i tried but i am running into bunch of issues
SELECT
distinct tblsmsgroups.GroupName,
tblsmsphones.PhoneNumber,
tblsmsphones.GroupID,
count(case when status = 1 then) as active
FROM
tblsmscustomers,
tblsmsgroups
INNER JOIN tblsmsphones
ON tblsmsgroups.groupID = tblsmsphones.GroupID
status = 1 then active how many, and status = 0 , then counts of how many, i am still stuck at the syntactical difference as to how can i do, any idea?
Well, if you want to add stuff up, you want a group by.
If you want to join tables, you want join -- never use commas for that purpose.
That suggest something like:
SELECT g.GroupName, p.PhoneNumber, p.GroupID,
SUM(status = 1) as active,
SUM(status = 0) as not_active
FROM tblsmsgroups g JOIN
tblsmsphones p
ON g.groupID = p.GroupID JOIN
tblsmscustomers c
ON c.customerID = p.customerID -- have to guess the JOIN condition here
GROUP BY g.GroupName, p.PhoneNumber, p.GroupID;
Note: Your question does not have enough information to understand the join conditions between the three tables; the above is a reasonable guess.
You probably want something like this:
SELECT
tblsmsgroups.GroupName,
tblsmsphones.PhoneNumber,
tblsmsphones.GroupID,
sum(status = 1) as active_count,
sum(status != 1) as inactive_count
FROM
tblsmscustomers ,
tblsmsgroups
INNER JOIN tblsmsphones ON tblsmsgroups.groupID = tblsmsphones.GroupID
GROUP BY GroupName, PhoneNumber, GroupID

SUM case returns value without GROUP BY

When I add SUM around my case select, it returns the summed value without the GROUP BY.
The query I am using, without the SUM, is the following
SELECT CASE WHEN subscription_types.type = 'Succes lidmaatschap' THEN 7 ELSE 8 END FROM subscription_used
INNER JOIN training_sessions ON training_sessions.id = subscription_used.training_session_id
INNER JOIN training_series AS tserie ON tserie.id = training_sessions.training_serie_id
INNER JOIN user_training_session ON user_training_session.training_session_id = training_sessions.id
INNER JOIN subscriptions ON subscriptions.id = subscription_used.subscription_id
INNER JOIN subscription_types ON subscription_types.id = subscriptions.subscription_type_id
WHERE subscription_used.training_session_id = (SELECT training_sessions.id FROM training_sessions WHERE DATE(event_start_date) = #week_2_ago_date AND training_serie_id = 17) AND present=1
GROUP BY subscriptions.id
This query returns the values: 8,7. However, when I put a SUM around the case, it gives me the number 75. 75 is the SUM of the values that are getting returned without the GROUP BY.
Any ideas on how to fix this problem so that the query gives me the correct value (8+7 = 15, 1 row)? Thanks in advance
Group by is implying distinct values based on subscription.id so probably if you take the group by you you will get something like 8,8,8,8,8,7,7,7,7,7 due to the joins and such.
With the group by you only get the distinct values of 8 and 7. When you do sum with the group it will sum all of them though not the 2 distinct.
Most Simple fix that will give you 15:
SELECT SUM(SUBSCRIPTION_USED) FROM (
SELECT CASE WHEN subscription_types.type = 'Succes lidmaatschap' THEN 7 ELSE 8 END FROM subscription_used
INNER JOIN training_sessions ON training_sessions.id = subscription_used.training_session_id
INNER JOIN training_series AS tserie ON tserie.id = training_sessions.training_serie_id
INNER JOIN user_training_session ON user_training_session.training_session_id = training_sessions.id
INNER JOIN subscriptions ON subscriptions.id = subscription_used.subscription_id
INNER JOIN subscription_types ON subscription_types.id = subscriptions.subscription_type_id
WHERE subscription_used.training_session_id = (SELECT training_sessions.id FROM training_sessions WHERE DATE(event_start_date) = #week_2_ago_date AND training_serie_id = 17) AND present=1
GROUP BY subscriptions.id);
Probably a better way to write it in general though.
EDIT: You could also do SUM(DISTINCT CASE......) if you only want to sum distinct values.

MySQL Count returns More Rows than it Should

I am attempting to count the number of rows from a given query. But count returns more rows than it should. What is happening?
This query returns only 1 row.
select *
from `opportunities`
inner join `companies` on `opportunities`.`company_id` = `companies`.`id`
left join `opportunityTags` on `opportunities`.`id` = `opportunityTags`.`opportunity_id`
where `opportunities`.`isPublished` = '1' and `opportunities`.`Company_id` = '1'
group by `opportunities`.`id` ;
This query returns that there are 3 rows.
select count(*) as aggregate
from `opportunities`
inner join `companies` on `opportunities`.`company_id` = `companies`.`id`
left join `opportunityTags` on `opportunities`.`id` = `opportunityTags`.`opportunity_id`
where `opportunities`.`isPublished` = '1' and `opportunities`.`Company_id` = '1'
group by `opportunities`.`id`;
When you select count(*) it is counting before the group by. You can probably (unfortunately my realm is SQL Server and I don't have a mySQL instance to test) fix this by using the over() function.
For example:
select count(*) over (partition by `opportunities`.`id`)
EDIT: Actually doesn't look like this is available in mySQL, my bad. How about just wrapping the whole thing in a new select statement? It's not the most elegant solution, but will give you the figure you're after.

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

Converting subquery to joins for performance

I have taken over a big project, and as the database is becoming large, some of the code stopped working,
Here is the query to find those rendering_requests who's last rending_log is pending, sometimes there are log entries which have no status change and recorded as noaction we dont need to count them. That is what I understood from the query.
SELECT
COUNT(rr.rendering_id) AS recordCount
FROM
rendering_request rr, rendering_log rl
WHERE
rl.rendering_id = rr.rendering_id
AND rl.status = 'pending' AND
rl.log_id = (
SELECT rl1.log_id
FROM rendering_log rl1
WHERE
rl.rendering_id = rl1.rendering_id AND
rl1.status = 'pending'
AND rl1.log_id = (
SELECT rl2.log_id
FROM rendering_log rl2
WHERE rl1.rendering_id = rl2.rendering_id AND rl2.status!='noaction'
ORDER BY rl2.log_id DESC LIMIT 1
)
ORDER BY rl1.log_id DESC
LIMIT 1
)
for example
rendering_id=1 is having multiple logs
status=noaction
status=noaction
status=pending
and
rendering_id=2 is having multiple logs
status=noaction
status=assigned
status=noaction
status=pending
when we run this query it should display count=1 as only the rendering_id=1 is our desired record.
Right now this query has stopped working, and it hangs the mysql server
Not 100% sure I have got this right, but something like this. Think you still need to use a couple of subselects but (depending on the version of MySQL) doing it this way with JOINs should be a lot faster
SELECT COUNT(rr.rendering_id) AS recordCount
FROM rendering_request rr
INNER JOIN rendering_log rl
ON rl.rendering_id = rr.rendering_id
INNER JOIN (SELECT rendering_id, MAX(log_id) FROM rendering_log WHERE status = 'pending' GROUP BY rendering_id) rl1
ON rl1.rendering_id = rl.rendering_id
AND rl1.log_id = rl.log_id
INNER JOIN (SELECT rendering_id, MAX(log_id) FROM rendering_log WHERE status!='noaction' GROUP BY rendering_id) rl2
ON rl2.rendering_id = rl1.rendering_id
AND rl2.log_id = rl1.log_id
WHERE rl.status = 'pending'