MySQL Join missing results - mysql

I am trying to select all users and get the amount of hours each user has logged from the joined times table. However, if the user has no times logged at all, I am not getting their name returned in the results. I would still like to get the name of every user, even if they have nothing logged in the times table.
I can get the results for all users if I remove the line:
AND (times.time_date BETWEEN '2014-03-01' AND '2014-03-31')
Here is my full query, What am I doing wrong?
SELECT users.user_id,
users.user_name,
SUM(times.time_used) as hours
FROM users
LEFT JOIN times on users.user_id = times.user_id
WHERE users.user_reminder = 1
AND users.status_id = 1
AND (times.time_date BETWEEN '2014-03-01' AND '2014-03-31')
GROUP BY users.user_id
ORDER BY users.user_name

Move your AND clause in ON() clause, using AND filter after WHERE clause will filter out the whole resultset,so if any user whose status_id is one and user_reminder is one but no time entries will be filtered,but if using AND in your on clause will join the users specific to the conditions and will return the uesrs even if they have no time entries
SELECT users.user_id,
users.user_name,
SUM(times.time_used) as hours
FROM users
LEFT JOIN times on (users.user_id = times.user_id
AND (times.time_date BETWEEN '2014-03-01' AND '2014-03-31') )
WHERE users.user_reminder = 1
AND users.status_id = 1
GROUP BY users.user_id
ORDER BY users.user_name

Related

How to join 3 tables and get sum from result of individual joins?

I have the following scenario:
Table: users : user_id, username ,...
Table: login: user_id, login_date, ...
Table: point: user_id, points, point_time
Joins will be on the basis of users.user_id with other tables.
Now, I want to get count of all the logins as well as sum of all the points earned by the user.
Now, when I do:
select users.user_id,count(*) from users
inner join login on users.user_id=login.user_id
group by users.user_id
It returns count as 36(for example).
Whenever I run:
select users.user_id,count(*),sum(points) from users
inner join point on users.user_id=point.user_id
group by users.user_id
It returns sum as 400(for example) and count as 2.
But if I combine both the queries:
select users.user_id,count(*),sum(points) from users
inner join login on users.user_id=login.user_id
inner join point on users.user_id=point.user_id
group by users.user_id
It returns count as 72 (36 * 2) and sum as 800 (400 *2).
Twice because of multiple userIds present.
I tried several things like combining with distincts but nothing seems to work. Please help.Better if it's possible with joins alone. Thanks in advance. I am using mysql with Php.
You can sum the points in a subquery and select distinct logins in the count
select users.user_id,l.login,p.points from users
inner join (select user_id, count(1) login from login
group by login) as l on users.user_id=login.user_id
inner join (select user_id, sum(point) as point
from point group by user_id ) as p on users.user_id=point.user_id
You should be able to do your count by joining in your login table and then including a subquery to get your count of points:
select users.user_id, count(*) as login_count,
(select sum(points) from point
where point.user_id = users.user_id) as points_sum
from users
inner join login on users.user_id=login.user_id
group by users.user_id

left join query does not return left table records if right table does not have records

I want to output users and their total number of wins and losses over requested date interval. When I run the query below within a date range that contains records in results table, everything works fine. However if a user does not have any records in results table for the requested date interval, then no user returned in the request at all. What I want is to return a user record anyway, even if the user does not have any records in results table for the requested date interval.
I believe the GROUP BY makes it behave that way, but I'm not sure how to configure the query to work the way I need it to.
SELECT
users.name,
users.division,
SUM(results.wins) as wins,
SUM(results.losses) as losses
FROM users LEFT JOIN results ON users.user_id = results.user_id
AND results.date BETWEEN {$startDate} AND {$endDate}
WHERE users.user_id = {$user_id} GROUP BY results.user_id;
The user is returned, just on a row where the id is NULL. You are grouping by the id in second table.
Instead, group by the first table field:
SELECT u.name, u.division,
SUM(r.wins) as wins, SUM(r.losses) as losses
FROM users u LEFT JOIN
results r
ON u.user_id = r.user_id AND r.date BETWEEN {$startDate} AND {$endDate}
WHERE u.user_id = {$user_id}
GROUP BY u.user_id;
---------^

Retrieve all users even if there is no user log entries

I want this query to show all users no matter what. And the calculate number of logins that happened in a certain time period (or just retun zero if there are no entries).
Right now, it only show users that have entries in the log in that time period.
SELECT
COUNT(user_log.type) AS logins,
users.name,
users.email,
users.user_id
FROM
users
LEFT JOIN
user_log
ON
users.user_id = user_log.user_id
WHERE
user_log.type = 8
AND
user_log.date >= :from
AND
user_log.date <= :to
GROUP BY
users.user_id
user_log.type = 8 means the log entry is a login
:from is a timestamp with the start date
:to is a timestamp with the end date
I use PDO
First of all, you're misusing a pernicious MySQL extension to GROUP BY. You'll have a hard time debugging. So you need to GROUP BY users.name, users.email. users.id.
The way your query is written, with items from your LEFT JOINed table in your WHERE clause, converts LEFT JOIN to INNER JOIN. So you're losing the unmatched rows from your first table.
Move those WHERE items to the ON clause and things will work the way you want. Like so.
SELECT
COUNT(user_log.type) AS logins,
users.name,
users.email,
users.user_id
FROM users
LEFT JOIN user_log
ON (
users.user_id = user_log.user_id
AND
user_log.type = 8
AND
user_log.date >= :from
AND
user_log.date <= :to
)
GROUP BY
users.name, users.email. users.id
If you use a where constraint on the second table with a LEFT JOIN, you'll have the same result as if you have used an INNER JOIN, because you don't allow NULL value.
Here a solution you can try, using a subrequest :
SELECT users.name,
users.email,
users.user_id,
IF (logins IS NOT NULL, logins, 0) AS logins
FROM users
LEFT JOIN (
SELECT user_id, COUNT(type) AS logins
FROM user_log
WHERE
type = 8
AND
date >= :from
AND
date <= :to
GROUP BY
user_id
) AS l ON l.user_id = users.user_id

Running an SQL query in the loop of another SQL query?

So I have the following query which fetches Active Competitions within an organisation and also tries to fetch the user who is leading the competition.
Currently the query correctly fetches the active competitions, and the totalPoints for each user. It now grabs all users, I only want it to grab the top user, so I am assuming the solution lies in the GROUP BY query, some sort of LIMIT?
In this image, you can see the results I am getting. As you can see, I am getting every user for each competition, where I only need the top user for each competition.
http://i.imgur.com/5OXen4e.png
Any idea on how I could solve this?
SELECT c.competitionId, c.name, c.start_date, c.end_date, a.userid, u.name, u.profilePic ,
SUM(activity_weight) AS totalPoints
FROM activity_entries a INNER JOIN users1 u ON u.id = a.userid INNER JOIN competitions c ON c.competitionId = a.competitionId
WHERE c.organisationId = 1 AND c.start_date < now() AND c.end_date > now()
GROUP BY a.userid, c.competitionId ORDER BY c.id DESC
There is a better way. Just run one query with and join to the competitions table. The second query you posted shows that you know how to join tables.

How to have a query with columns needing different conditions in MySQL?

For a game, I want to count the number of user sign-ups by hour (using MySQL.).
Quite easy, something like that:
SELECT COUNT(*), DAY(date_user), HOUR(date_user)
FROM users,
GROUP BY DAY(date_user), HOUR(date_user)
After that, I want to only take in consideration users which have played the game at least one time. I have a second table with scores.
SELECT COUNT(*), DAY(date_user), HOUR(date_user)
FROM users, scores
WHERE users.id = scores.userid
GROUP BY DAY(date_user), HOUR(date_user)
Great...
Now, I want the two queries to result in one table, like:
Global signups | Signups of playing users | Day | Hour
I have not found a working query for this yet. Should I use unions? Or joins?
Here's one way:
select
day(date_user)
, hour(date_user)
, count(distinct u.id) as GlobalSignups
, count(distinct s.userid) as SignupsOfPlayingUsers
from users u
left join scores s on u.id = s.userid
group by day(date_user), hour(date_user)
Counting distinct user id's gives the total number of users. When the left join fails, s.userid will be NULL, and NULLs are not counted by count(). So count(distinct s.userid) returns the number of users with signups.