Why is Count(*) returning unexpected number? - sql

This is my query:
Select COUNT(*)
From
Users u
Inner Join
UsersLoginHistory uh On u.UserID = uh.UserID
Where
1 = 1
And
u.AccountID = 37
Group By u.UserID
What I'd like to be able to get is Count(*) should be returning a number after grouping on u.UserId. But it returns the Count(*) before the group by is made.
So I can rewrite the above query as:
Select COUNT(*)
From (
Select u.Username
From
Users u
Inner Join
UsersLoginHistory uh On u.UserID = uh.UserID
Where
1 = 1
And
u.AccountID = 37
Group By u.UserID
) v
But I need to find out why is the Count(*) returning records before a group by is made and how can I fix the 1st query itself.
EDIT: Sample Records
Users table
UserId Username
102 tom.kaufmann
UserLoginHistory table
UsersLoginHistoryID UserID LoginDateTime LogoutDateTime IPAddress
1 102 2012-09-28 01:16:00 NULL 115.118.71.248
2 102 2012-09-28 01:29:00 2012-09-28 01:29:00 127.0.0.1
3 102 2012-09-28 01:32:00 2012-09-28 01:32:00 127.0.0.1
4 102 2012-09-28 01:41:00 NULL 115.118.71.248
5 102 2012-09-28 01:43:00 2012-09-28 07:04:00 115.118.71.248
and so on..
Haven't writted every single record in this DB.

Based on your second query which you say returns the desired results (and assuming UserID is the PK of Users) I presume this is what you need
SELECT Count(UserID)
FROM Users u
WHERE u.AccountID = 37
AND EXISTS (SELECT *
FROM UsersLoginHistory uh
WHERE u.UserID = uh.UserID)
This will be more efficient than expanding out all the joined rows then collapsing them again with Group By u.UserID and counting the number of rows that result.

Change the first line to:
Select COUNT(*), u.UserID

This should provide you a list of UserIds and the count of entries in the UsersLoginHistory table.
SELECT u.UserId
, COUNT(uh.*)
FROM Users u
INNER JOIN UsersLoginHistory uh ON u.UserID = uh.UserID
WHERE u.AccountID = 37
GROUP BY u.UserID

But I need to find out why is the Count(*) returning records before a group by is made and how can I fix the 1st query itself
It is counting the number of lines for each UserID (number of logins), which is exactly how group by is supposed to work.

COUNT is an aggregate function and this is how it's supposed to work. You get count per grouping.
In your first query you are querying the number of userloginhistory per user. In your second query you are querying number of users with login history.
http://msdn.microsoft.com/en-us/library/ms173454.aspx

Related

How to sum up max values from another table with some filtering

I have 3 tables
User Table
id
Name
1
Mike
2
Sam
Score Table
id
UserId
CourseId
Score
1
1
1
5
2
1
1
10
3
1
2
5
Course Table
id
Name
1
Course 1
2
Course 2
What I'm trying to return is rows for each user to display user id and user name along with the sum of the maximum score per course for that user
In the example tables the output I'd like to see is
Result
User_Id
User_Name
Total_Score
1
Mike
15
2
Sam
0
The SQL I've tried so far is:
select TOP(3) u.Id as User_Id, u.UserName as User_Name, SUM(maxScores) as Total_Score
from Users as u,
(select MAX(s.Score) as maxScores
from Scores as s
inner join Courses as c
on s.CourseId = c.Id
group by s.UserId, c.Id
) x
group by u.Id, u.UserName
I want to use a having clause to link the Users to Scores after the group by in the sub query but I get a exception saying:
The multi-part identifier "u.Id" could not be bound
It works if I hard code a user id in the having clause I want to add but it needs to be dynamic and I'm stuck on how to do this
What would be the correct way to structure the query?
You were close, you just needed to return s.UserId from the sub-query and correctly join the sub-query to your Users table (I've joined in reverse order to you because to me its more logical to start with the base data and then join on more details as required). Taking note of the scope of aliases i.e. aliases inside your sub-query are not available in your outer query.
select u.Id as [User_Id], u.UserName as [User_Name]
, sum(maxScore) as Total_Score
from (
select s.UserId, max(s.Score) as maxScore
from Scores as s
inner join Courses as c on s.CourseId = c.Id
group by s.UserId, c.Id
) as x
inner join Users as u on u.Id = x.UserId
group by u.Id, u.UserName;

select all row values as a list

I have a table tasks that looks like this:
userId caption status id
1 Paul done 1
2 Ali notDone 18
3 Kevin notDone 12
3 Elisa notDone 13
I join it with another table users to find the number of taskswhere status = notDone. I do it like this:
SELECT u.id,
t.number_of_tasks,
FROM users u
INNER JOIN (
SELECT userId, COUNT(*) number_of_tasks
FROM tasks
WHERE status = "notDone"
GROUP BY userId
) t ON u.id = t.userId
"""
Now, I want create another column captions that somehow includes a list of all captions that were included in the countand fulfil the join + where conditions.
For example, I would expect this as one of the rows. How can I achieve this?
userId number_of_tasks captions
3 2 ["Kevin", "Elisa"]
You can use json_group_array() aggregate function inside the subquery to create the list of captions for each user:
SELECT u.id, t.number_of_tasks, t.captions
FROM users u
INNER JOIN (
SELECT userId,
COUNT(*) number_of_tasks,
json_group_array(caption) captions
FROM tasks
WHERE status = 'notDone'
GROUP BY userId
) t ON u.id = t.userId;

SQL join query, want to get the latest record of joining table (or null if it doesn't match)

I have 2 tables
User:
ID
NAME
1
John
2
Jane
3
Jim
login:
id
date
1
2021-01-29
3
2021-02-27
1
2021-03-11
3
2021-04-18
I want to get the result like:
name
date
John
2021-03-11
Jane
null
Jim
2021-04-18
How shall I write the SQL query?
I tried quite a few join but never got the 2nd record (Jane/Null) out from the query. Need some help here, thanks a ton in advance!
You can use left join and group by:
select u.id, u.name, max(l.date)
from user u left join
login l
on l.id = u.id
group by u.id, u.name;
Note: This includes the id as well. If name is known to be unique that is not necessary.
complimenting #gordons answer another option is to use a subquery like follows
SELECT user.name, login.last_login
FROM user
LEFT JOIN (SELECT id, max(date) AS last_login FROM logins group by id) AS login
ON login.id = user.id

SQL Statement to Join Users and Last Login Date,Location

I have two tables, a list of Users, and a UserLoginHistory table that has their history of login dates, IP Addresses, and Geolocations.
I want to create one SQL statement that will return one each User + their last LoginDate and Geolocation.
Users.UserId, Users.Name
100 Bill
101 Steve
UserLoginHx.UserId, UserLoginHx.LoginDate, UserLoginLocation
100 1/1/2018 New York
101 1/1/2018 Los Angeles
100 1/4/2018 Chicago
101 1/5/2018 Denver
....
Result desired in this example should return two rows as:
100 Bill 1/4/2018 Chicago
101 Steve 1/5/2018 Denver
Thanks. (so far nobody got close)
Try below query:
select UserLoginHx.UserId,users.name,a.logindate,a.location from UserLoginHx
inner join
(select UserLoginHx.UserId,max(UserLoginHx.LoginDate) as logindate
from UserLoginHx
group by UserLoginHx.UserId)a on a.UserId=UserLoginHx.UserId and a.logindate=UserLoginHx.LoginDate
inner join Users on Users.UserId=UserLoginHx.UserId
SELECT u.UserId, u. Name, ulh.LoginDate, ulh.UserLoginLocation
FROM Users u
JOIN UserLoginHistory ulh ON u.UserId = ulh.UserId
A JOIN clause is used to combine rows from two or more tables, based on a related column between them.
so for you need join two tables
SELECT u.*, uh.LoginDate, uh.UserLoginLocation
FROM Users u
JOIN UserLoginHistory uh ON u.UserId = uh.UserId
Try the following query-:
with cte as
(
select a.*,LoginDate,UserLoginLocation,
ROW_NUMBER() over (partition by a.UserId order by Login desc) rn
from Users a
join UserLoginHistory b
on a.UserId=b.UserId
)select * from cte where rn=1
SQL Server

Where clause in sql query remove also null values

I have these tables:
'Users'
id email name last_access
1 luca#gmail.com Luca Pluto 2012-10-05 17:21:22.0
2 pippo#gmail.com Irene Pippo 2012-10-05 17:22:25.0
'Nets_permissions'
user_id network_id perm
1 1234 3
1 1235 1
2 1235 3
I've written this query:
SELECT u.id, u.name, n.perm
FROM users as u LEFT OUTER JOIN nets_permissions as n
ON u.id = n.user_id
WHERE n.network_id=1234 AND n.perm <> 3
because I want users that already have a permission for network_id (1234) , but also users that don't have a permission for the network_id. In other words I would have this result query:
2 null null
because the user Luca Pluto with id=1, for the net 1234, have perm=3 so I want left out. Instead, the user Irene Pippo with id=2 doesn't have any permission on the 1234 net. So it's row must have net_id and perm set to null.
My query result is empty. I don't know why. Without the clause n.perm <> 3 seems to work well, but after also the null value are left out, not only the rws with perm=3.
I've also tried in this way:
SELECT u.id, u.name, n.perm FROM users as u LEFT OUTER JOIN
(select * from nets_permissions WHERE network_id=1234) as n on u.id = n.user_id
WHERE n.perm <> 3
but it doesn't work. without the WHERE clause all works. After no. The result query is empty.
How I can resolve this problem? I need that the perm column is a value or null, I can't remove this column.
The right solution is:
SELECT u.id, u.name, n.perm FROM users as u LEFT OUTER JOIN
(select * from nets_permissions WHERE network_id=1234) as n on u.id = n.user_id
WHERE (n.perm <> 3 or n.perm is NULL)
Here Working with NULL Values there is a expanation of the treatment of NULL values in MySQL
Thank you all for your help!
Many SQL dialect use a special trinary logic with NULL the third possible value additional to true and false.
Anything compared to NULL results to NULL which in turn is handled as false.
So x = null will result in false for all x, just as x != null
If you want to include NULL values in the result you have to add special handling for that (Oracle syntax):
SELECT u.id, u.name, n.perm
FROM users as u LEFT OUTER JOIN nets_permissions as n
ON u.id = n.user_id
WHERE n.network_id=1234
AND (n.perm <> 3 or n.perm is not null)
SELECT u.id, u.name, n.perm
FROM
users as u
LEFT OUTER JOIN
nets_permissions as n ON u.id = n.user_id
WHERE
n.network_id = 1234 and n.perm <> 3
or
n.network_id <> 1234
or
n.network_id is null
I like users that user the "JOIN" syntax. It has additional benefit, aside from making the code easier to read, that people don't realize.
SELECT u.id, u.name, n.perm
FROM users as u LEFT OUTER JOIN nets_permissions as n
ON u.id = n.user_id
AND n.network_id=1234
WHERE COALESCE(n.perm,-1) <> 3