SUM of a field by DISTINCT rows - mysql

I want to be able to get a sum of a field 'count' on distinct row id.
Below is my query for it. How can we achieve this?
SELECT l.sid, l.Title, u.CompanyName, l.activation_date, l.expiration_date, COUNT(s.count), COUNT(DISTINCT a.id)
FROM listings l
LEFT JOIN statistics s
ON s.object_sid = l.sid
LEFT JOIN users u
ON u.sid = l.user_sid
LEFT JOIN applications a
ON a.listing_id = l.sid
WHERE l.sid = 6276
AND s.event = 'viewListing'
GROUP BY l.sid
LIMIT 10

If you need the sum of the column statistics.count by "listing" and by "application", then you need to GROUP BY both "listing" and "application":
SELECT
l.sid, l.Title, l.activation_date, l.expiration_date,
u.CompanyName,
SUM(s.count), COUNT(a.id) -- <= SUM(s.count)
FROM listings l
LEFT JOIN statistics s ON s.object_sid = l.sid
LEFT JOIN users u ON u.sid = l.user_sid
LEFT JOIN applications a ON a.listing_id = l.sid
WHERE l.sid = 6276
AND s.event = 'viewListing'
GROUP BY l.sid, a.sid -- <= GROUP BY clause modified

Related

Display results which have no count/zero as well

I am trying to get a count of the number of logins during a given timeframe, currently my SQL query displays only results that had at least one login, I'd like it to display even those which have zero logins.
Query i'm using:
SELECT c.FullName, COUNT(l.Id)
FROM LoginsTable l JOIN UsersTable u ON u.Email = l.Email JOIN Organisations c ON c.Id = u.OrganisationId
WHERE l.AttemptTime > "2019-10-01" AND l.AttemptTime < "2019-11-01" AND l.Success = 1
GROUP BY c.Name
ORDER BY c.Name ASC;
You have a few issues. Firstly, you either need to use a RIGHT JOIN from LoginsTable or reorder the JOINs to put the JOIN to LoginsTable last and use a LEFT JOIN. Given the nature of your query the latter probably makes more sense.
Secondly, you need to put any conditions on fields from a table which has been LEFT JOINed into the join condition, otherwise MySQL converts the LEFT JOIN into an INNER JOIN (see the manual). Finally, you should GROUP BY the same fields as specified in your SELECT. This should work:
SELECT c.FullName, COUNT(l.Id)
FROM Organisations c
JOIN UsersTable u ON u.OrganisationId = c.Id
LEFT JOIN LoginsTable l ON u.Email = l.Email AND l.AttemptTime > "2019-10-01" AND l.AttemptTime < "2019-11-01" AND l.Success = 1
GROUP BY c.FullName
ORDER BY c.FullName
I found 2 issues here:
your group by column is not listed on your column
date condition is using double quotes.
try below query.
SELECT c.FullName, COUNT(l.Id)
FROM LoginsTable l
LEFT JOIN UsersTable u ON u.Email = l.Email
LEFT JOIN Organisations c ON c.Id = u.OrganisationId
WHERE l.AttemptTime between '2019-10-01' AND '2019-11-01' AND l.Success = 1
GROUP BY c.FullName
ORDER BY c.FullName ASC;
As Roman Hocke said you need to use left join as below :
SELECT c.FullName, COUNT(l.Id)
FROM UsersTable u
JOIN Organisations c ON c.Id = u.OrganisationId
LEFT JOIN LoginsTable l ON u.Email = l.Email
WHERE l.AttemptTime > "2019-10-01" AND l.AttemptTime < "2019-11-01" AND l.Success = 1
GROUP BY c.Name
ORDER BY c.Name ASC;
Moreover, you should fix your group by or select using the same field : SELECT c.Name or GROUP BY c.FullName ORDER BY c.FullName
EDIT : Nick's answer is the one. As he said perfectly well, you need to put your conditions in the on clause of your left join.
SELECT c.FullName, COUNT(l.Id)
FROM UsersTable u
JOIN Organisations c ON c.Id = u.OrganisationId
LEFT JOIN LoginsTable l ON (u.Email = l.Email AND l.AttemptTime > "2019-10-01" AND l.AttemptTime < "2019-11-01" AND l.Success = 1)
GROUP BY c.FullName
ORDER BY c.FullName ASC;

mysql count from joins using 3 tables

i'm trying to build a chat application, in which i have these four tables
chat --id,group_id,body,time_posted[timestamp]
chat_groups --establish id,name for a group
users_groups --link users to a group, also define if user stared this group FIELDS::user_id,group_id,stared[bool]
wall_visit --user_id,group_id,last_visit[timestamp]
the idea is every users join a group, and they post to it in chat.
chat_groups table is just for defining the room, while users_groups is for setting access of members to this group.
wall_visit table is a table that store specific user last time accessed specific group (since its many to many u know..)
now what im trying to establish is to get in one query,
the chat_groups the user in relation with
the count of messages posted to this group since user last login (from settings)
the count of members in this group
the group name
:)
i have been trying to hours now :( best i could come up with
SELECT w.last_visit,access.stared,cg.user_id,u.fullname as username,cg.name as group_name ,cgu.count_members,c.count_msgs,c.time_posted
FROM `chat_groups`cg
inner join chat_groups_users access on (access.group_id = cg.id and access.user_id = ?)
left outer join users u on u.id = cg.user_id
left join wall_visit w on w.group_id = cg.id
left join (select count(*) as count_members,group_id from group_users group by group_id) cgu on cgu.group_id = cg.id
left join (SELECT count(wv.id) as count_msgs,c.group_id,c.time_posted FROM chats c
left outer join `wall_visit` wv on (wv.group_id is not null and c.group_id = wv.group_id and c.time_posted > wv.last_visit)
group by c.group_id) c on c.group_id = cg.id
where cg.user_id = 1
this query is working ..ehh, my main problem is with the count of the messages in the group since last_visit.
what is the best methode to get message_count to work :( ??
can this query be optimized more?
Thanks SO community :)
My 2nd attempt
SELECT w.last_visit,access.stared,cg.user_id,u.fullname as username,cg.with_id,uu.fullname as with_name,cg.name as group_name ,cgu.count_members,c.count_msgs,c.time_posted
FROM `chat_groups`cg
inner join chat_groups_users access on (access.group_id = cg.id and access.user_id = 1)
left outer join users u on u.id = cg.user_id
left join wall_visit w on w.chat_id = cg.id
left outer join users uu on uu.id = cg.with_id
left join (select count(*) as count_members,group_id from chat_groups_users group by group_id) cgu on cgu.group_id = cg.id
left join (
SELECT group_id,count(c.id) as count_msgs,time_posted FROM `chats` c inner join wall_Visit wv on wv.chat_id = c.group_id where c.id > wv.last_visit group by c.group_id
) c on c.group_id = cg.id
where cg.user_id = 1
this should fix you count message problem
SELECT
`cg`.`user_id`, `cg`.`with_id`, `cg`.`name` AS `group_name`,
`access`.`stared`,
`u`.`fullname` AS `username`,
`w`.`last_visit`,
`uu`.`fullname` AS `with_name`,
`cgu`.`count_members`,
`c`.`count_msgs`, `c`.`time_posted`
FROM `chat_groups` AS `cg`
INNER JOIN `chat_groups_users` AS `access`
ON (`access`.`group_id` = `cg`.`id` AND `access`.`user_id` = `cg`.`user_id`)
LEFT OUTER JOIN `users` AS `u`
ON (`u`.`id` = `cg`.`user_id`)
LEFT JOIN `wall_visit` AS `w`
ON (`w`.`chat_id` = `cg`.`id`)
LEFT OUTER JOIN `users` AS `uu`
ON (`uu`.`id` = `cg`.`with_id`)
LEFT JOIN (
SELECT COUNT(*) AS `count_members`, `group_id`
FROM `chat_groups_users`
GROUP BY
`group_id`
) AS `cgu`
ON (`cgu`.`group_id` = `cg`.`id`)
LEFT JOIN (
SELECT count(`c`.`id`) AS `count_msgs`, `c`.`time_posted`
FROM `chats` AS `c`
INNER JOIN `wall_visit` AS `wv`
ON (`wv`.`chat_id` = `c`.`group_id`)
WHERE
`c`.`time_posted` > `wv`.`last_visit`
GROUP BY
`c`.`group_id`
) AS `c`
ON (`c`.`group_id` = `cg`.`id`)
WHERE `cg`.`user_id` = 1
otherwise u have to setup a fiddle

mysql join with multiple tables and count query

I have total 6 tables in which different info has been saved
Now i need a result in which get count from 5 tables and select all info from main table but if record does not exist than it must be need to return 0 instead of no row found that's the problem here
I have tried below query but didn't get success
SELECT
u.*,
COUNT(DISTINCT c.id) as comments,
COUNT(DISTINCT d.id) as dislikes,
COUNT(DISTINCT l.id) as likes,
COUNT(DISTINCT s.id) as shares,
COUNT(DISTINCT t.id) as tags
FROM
job_details as u
JOIN job_comments as c ON u.id = c.job_id
JOIN job_dislike as d ON u.id = d.job_id
JOIN job_like as l ON u.id = l.job_id
JOIN job_share as s ON u.id = s.job_id
JOIN job_tags as t ON u.id = t.job_id
WHERE
u.id = c.job_id AND
u.id = d.job_id AND
u.id = l.job_id AND
u.id = s.job_id AND
u.id = t.job_id
GROUP BY
u.id
This query is executed, but didn't get exact result.
I don't quite understand why.
I was hoping somebody here could help me out?
Thanks!
You probably didn't get the exact result because some tables may be missing values.
Although you can solve this problem with a LEFT JOIN, the safer solution is to pre-aggregate the data:
SELECT u.*, c.comments, d.dislikes, l.likes, s.shares, t.tags
FROM job_details as u LEFT JOIN
(select c.job_id, count(*) as comments from job_comments group by c.job_id
) c
ON u.id = c.job_id LEFT JOIN
(select d.job_id, count(*) as dislikes from job_dislike d group by d.job_id
) d
ON u.id = d.job_id LEFT JOIN
(select l.job_id, count(*) as likes from job_like l group by l.job_id
) l
ON u.id = l.job_id LEFT JOIN
(select s.job_id, count(*) as shares from job_share s group by s.job_id
) s
ON u.id = s.job_id LEFT JOIN
(select t.job_id, count(*) as tags from job_tags t group by t.job_id
) t
ON u.id = t.job_id;
Why is this better? Consider an id that has 5 comments, likes, dislikes, shares and tags. The JOIN approach produces an intermediate result with 5*5*5*5*5 = 3,125 intermediate rows. Things can really get out of hand for popular ids.
Use LEFT JOIN instead of JOIN. and you don't need WHERE clause since you have joined those tables. And, use IFNULL function to return 0 for null values. You need to modify you query like this :
SELECT u.id,
IFNULL(COUNT(DISTINCT c.id),0) as comments,
IFNULL(COUNT(DISTINCT d.id),0) as dislikes,
IFNULL(COUNT(DISTINCT l.id),0) as likes,
IFNULL(COUNT(DISTINCT s.id),0) as shares,
IFNULL(COUNT(DISTINCT t.id),0) as tags
FROM job_details as u
LEFT JOIN job_comments as c ON u.id = c.job_id
LEFT JOIN job_dislike as d ON u.id = d.job_id
LEFT JOIN job_like as l ON u.id = l.job_id
LEFT JOIN job_share as s ON u.id = s.job_id
LEFT JOIN job_tags as t ON u.id = t.job_id
GROUP BY u.id

Problems with reusing LEFT JOIN results in WHERE and ORDER BY Clause

SELECT s.*,
u.username,
u.fullname,
c.title AS ctitle,
c.description AS cdescription,
sa.attention,
sp.popularity,
COUNT(DISTINCT f.id) AS favorites,
COUNT(DISTINCT st.id) AS stars,
COUNT(DISTINCT v.id) AS views
FROM shots s
INNER JOIN users u ON u.id = s.user_id
INNER JOIN categories c ON c.id = s.cat_id
LEFT OUTER JOIN(
SELECT shot_id, round(AVG(count),2) AS attention
FROM points
WHERE date > DATE_SUB(CURDATE(),INTERVAL 2 DAY)
GROUP BY shot_id
) sa ON sa.shot_id = s.id
LEFT OUTER JOIN(
SELECT shot_id, SUM(count) AS popularity
FROM points
GROUP BY shot_id
) sp ON sp.shot_id = s.id
LEFT OUTER JOIN favorites f ON f.shot_id = s.id
LEFT OUTER JOIN stars st ON st.shot_id = s.id
LEFT OUTER JOIN views v ON v.shot_id = s.id
**WHERE s.library = 1 AND sa.attention > 40
ORDER BY sa.attention DESC
LIMIT 0,50**
GROUP BY s.id
I can't use the sa.attention in a condition and for ordering. Why?
(I removed the marked part, and the query works!)
What do I have to change in my Query? And if you could give a explanation for it, that would be very nice!
You are negating your OUTER JOIN by putting that in your WHERE criteria. Move it to your JOIN and you'll get your NULL records back:
SELECT s.*,
u.username,
u.fullname,
c.title AS ctitle,
c.description AS cdescription,
sa.attention,
sp.popularity,
COUNT(DISTINCT f.id) AS favorites,
COUNT(DISTINCT st.id) AS stars,
COUNT(DISTINCT v.id) AS views
FROM shots s
INNER JOIN users u ON u.id = s.user_id
INNER JOIN categories c ON c.id = s.cat_id
LEFT OUTER JOIN(
SELECT shot_id, round(AVG(count),2) AS attention
FROM points
WHERE date > DATE_SUB(CURDATE(),INTERVAL 2 DAY)
GROUP BY shot_id
) sa ON sa.shot_id = s.id AND sa.attention > 40
LEFT OUTER JOIN(
SELECT shot_id, SUM(count) AS popularity
FROM points
GROUP BY shot_id
) sp ON sp.shot_id = s.id
LEFT OUTER JOIN favorites f ON f.shot_id = s.id
LEFT OUTER JOIN stars st ON st.shot_id = s.id
LEFT OUTER JOIN views v ON v.shot_id = s.id
WHERE s.library = 1
GROUP BY s.id
ORDER BY sa.attention DESC
LIMIT 0,50
A second note, GROUP BY cannot go at the end. I moved that to the correct spot as well.

MySQL query with and without WHERE condition at once

How can I select both values at once? For example, I have a Lesson that have Students and each Student is linked to a Client, so what I want to achieve is something like:
SELECT l.id,
l.value * clientStudents/totalStudents as total
FROM Lesson l
JOIN lesson_student ls ON l.id = ls.lesson_id
JOIN Student s ON ls.student_id = s.id
JOIN Client c ON s.client_id = c.id
**WHERE c.id = <SOME_CLIENT>**
being clientStudents the count using the WHERE clause and totalStudents without using it.
You can move the condition in the calculation phase. Something like:
SELECT l.id,
l.value * SUM(if(c.id = <SOME_CLIENT>,clientStudents,0)) / SUM(totalStudents) as total
FROM Lesson l
JOIN lesson_student ls ON l.id = ls.lesson_id
JOIN Student s ON ls.student_id = s.id
JOIN Client c ON s.client_id = c.id
GROUP BY l.id, l.value