SQL - Finding unique values on two tables - sql

I have two tables where I want to find
a. distinct usernames, non-distinct document#, and non-distinct location names
b. distinct usernames, non-distinct document#, and distinct location names
These are two separate sql queries.
Here is what those tables would look like:
User Table
|---------------------------------------|------------|
| UserId | UserName |Document#| LocationId |
|---------------------------------------|------------|
| 1 | bob2# | DL | 1 |
|---------------------------------------|------------|
| 2 | mary3# | Passport| 2 |
|---------------------------------------|------------|
| 3 | bob2# | SIN# | 4 |
|---------------------------------------|------------|
| 4 | sam5# | DL | 3 |
|---------------------------------------|------------|
| 5 | bob2# | SIN# | 1 |
|---------------------------------------|------------|
Location Table
|---------------------------------------|
| LocationId | UserId |LocName |
|---------------------------------------|
| 1 | 1 | Denvor |
|---------------------------------------|
| 2 | 2 | NY |
|---------------------------------------|
| 3 | 3 | San Fran |
|---------------------------------------|
| 4 | 4 | Chicago |
|---------------------------------------|
This is what I've tried, for part a)
select User.UserName, User.Document#, Location.LocName
from User Inner Join
Location
On User.UserId = Location.LocationId
Group by User.UserName
This is for part b)
select User.UserName, User.Document#, Location.LocName
from User Inner Join
Location
On User.UserId = Location.LocationId
Group by User.UserName, Location.LocName
Can someone shed some light as to how to approach this?

You need aggregation with your group by
Example you can group by User.UserName and get number of Documents (Count), and Count of Location.LocName
select User.UserName, Count(User.Document#), Count(Distinct Location.LocName)
from User
Inner Join Location On User.UserId = Location.LocationId
Group by User.UserName
Edit :
I think you are doing the join wrong as you joining User ID to Location ID
It should be
select User.UserName, Count(User.Document#), Count(Distinct Location.LocName)
from User
Inner Join Location On User.LocationId = Location.LocationId
Group by User.UserName

Related

SQL: How to find rows in one table that have no references to rows in another tables?

I have three tables: users, rooms, room_users.
Users can have many rooms and rooms as well can have many users, so this is many to many relationship.
users table:
+----+-----------+-----+
| id | name | age |
+----+-----------+-----+
| 1 | Christian | 19 |
| 2 | Ben | 36 |
| 3 | Robert | 52 |
| 4 | Monica | 25 |
| 5 | Alice | 26 |
| 6 | William | 18 |
+----+-----------+-----+
rooms table:
+----+----------+
| id | name |
+----+----------+
| 1 | College |
| 2 | Work |
| 3 | Football |
+----+----------+
And room_users table that represents relationship between users and rooms:
+---------+---------+
| user_id | room_id |
+---------+---------+
| 1 | 1 |
| 1 | 3 |
| 2 | 2 |
| 4 | 1 |
| 5 | 2 |
| 6 | 1 |
| 6 | 3 |
+---------+---------+
So, having these tables we can say that:
Christian(1) belongs to College(1) and Football(3) rooms.
Ben(2) belongs to Work(2) room.
Robert(3) does not belong to any room.
Monica(4) belongs to College(1) room.
Alice(5) belongs to Work(2) room.
William(1) belongs to College(1) and Football(3) rooms.
And now if I want to find users (ids) that does belong to Football room I should use this query:
SELECT user_id FROM room_users WHERE room_id = 3
Output for this query:
+---------+
| user_id |
+---------+
| 1 |
| 6 |
+---------+
This is correct, only Christian(1) and William(3) belong to Football room.
But how to find users that does NOT belong to Football room?
In this case, query must return 2, 3, 4 and 5 ids. That is, all IDs excluding IDs from the first query.
Is it possible to do it using LEFT JOIN?
As far as I know, it is more efficient way than using sub-queries.
Thanks in advance!
EDIT:
I've found a query that can solve the problem, but this query is VERY SLOW on large database:
SELECT users.id FROM users WHERE 0=(SELECT COUNT(*) FROM room_users WHERE user_id=users.id AND room_id=3);
Without correlated behavior, try something like this:
SELECT u.*
FROM users AS u
LEFT JOIN (
SELECT DISTINCT user_id FROM room_users WHERE room_id = 3
) AS v
ON v.user_id = u.id
WHERE v.user_id IS NULL
;
For performance issues, start by reviewing the explain/execution plan and use of indexes.
You could find those users that belong to the football room AND then exclude those using not in.
Also you can use a JOIN
SELECT u.*
FROM
users u
WHERE user_id NOT IN
(SELECT user_id FROM room_users WHERE room_id=3)
You are correct that this is possible to do with a left join.
SELECT
u.id
FROM
users u
LEFT JOIN room_users ur
ON u.id = ur.user_id
AND ur.room_id = 3
WHERE
ur.room_id is null;

JOIN the table if records exist

is it possible if i want to do INNER JOIN only if the record exist on the 2nd table if not then dont join?
this is my table
User table
+--------+--------------+
| id | name |
+--------+--------------+
| 1 | John |
+--------+--------------+
| 2 | Josh |
+--------+--------------+
House table
+--------+-------------+--------------+
| id | owner_id | house_no |
+--------+-------------+--------------+
| 1 | 1 | 991 |
+--------+-------------+--------------+
this is my INNER JOIN query
SELECT h.owner_id, u.name, h.house_no FROM user u
INNER JOIN house h on u.id = h.owner_id
WHERE u.id = :id
it will return this result if id = 1
+--------+--------------+--------------+
| id | name | house_no |
+--------+--------------+--------------+
| 1 | John | 991 |
+--------+--------------+--------------+
but if i run with id = 2 no result returned.
what i want to do right now is it still return the result even when no data exist for id = 2 in table house
Use a left outer join instead.
SELECT u.id, u.name, h.house_no FROM user u
LEFT OUTER JOIN house h on u.id = h.owner_id
WHERE u.id = :id
The resulting record will be:
+--------+--------------+--------------+
| id | name | house_no |
+--------+--------------+--------------+
| 2 | Josh | null |
+--------+--------------+--------------+

Problems with joining two tables

I'm currently making an application using C# and I am having some difficulty with joining two tables. To make things more clear here are my table structures
Table 1(List of Employee)
| EmployeeID | EmployeeName |
+------------+--------------+
| 1 | John Smith |
| 2 | Ian Smosh |
Table 2(List of Referrals)
| PersonalID | InviterID | InterviewerID |
+------------+-----------+---------------+
| 1 | 1 | 1 |
| 2 | 1 | 2 |
The output on Datagridview should be
| Employee Name | Invites | Interviews |
+---------------+---------+------------+
| John Smith | 2 | 1 |
| Ian Smosh | 0 | 1 |
I am currently able to get the invites but not the interviews at the same time. I am only able to get one.
Here is what I get
| Employee Name | Invites |
+---------------+---------+
| John Smith | 2 |
| Ian Smosh | 0 |
Here is my code:
SELECT Table1.RecruiterName AS Name,
COUNT(Table2.InviterID) AS Invites,
COUNT(Table2.InterviewID) AS Interviews
FROM Table2 LEFT JOIN Table1 ON Table2.InviterID = Table1.EmployeeID
AND Table2.InterviewerID = Table1.InviterID
GROUP BY EmployeeName
Anyone there knows what's wrong with my code?
UPDATE: I managed to make it a little better but I keep getting
| Employee Name | Invites | Interviews |
+---------------+---------+------------+
| John Smith | 2 | 2 |
| Ian Smosh | 0 | 1 |
The entry for John Smith only has 2 Invites and 1 Interview. This is my current code
SELECT Recruiters.RecruiterName AS Name, COUNT(Source.SourceID) AS Source, COUNT(Interview.InterviewID) AS Interview
FROM Recruiters
LEFT JOIN Hires Source ON Source.SourceID=Recruiters.RecruiterID
LEFT JOIN Hires Interview ON Interview.InterviewID=Recruiters.RecruiterID
GROUP BY RecruiterName
Why is it that John Smith gets a wrong amount in interviews but Ian Smosh is correct.
the double join is double dipping
this should work
select employee.EmployeeName, inv.count, int.count
from employee
join ( select InviterID,
count(*) as count
from referral
group by InviterID ) as inv
on employee.employeeID = inv.InviterID
join ( select InterviewerID,
count(*) as count
from referral
group by InterviewerID ) as int
on employee.employeeID = int.InterviewerID
SELECT Recruiters.RecruiterName AS Name,
(select COUNT(*) from Hires where SourceID = Recruiters.RecruiterID) AS Source,
(select COUNT(*) from Hires where InterviewID = Recruiters.RecruiterID) AS Interview
FROM Recruiters

Inner queries on the same table - is there a better way?

I have these two tables (simplified versions)
Orders
owner_id | user_1 | user_2 | amount | order_id
-----------------------------------------------
1 | 2 | 3 | 100 | AAA
1 | 7 | 2 | 200 | BBB
2 | 3 | 5 | 400 | CCC
Users
user_id | username
------------------
1 | John
2 | Robert
3 | Sally
4 | Mario
5 | Albert
6 | Hernest
7 | Homer
I need to get, in one query, all the info related to a particular order, including the owner_id, user_1, user_2 usernames.
The result I'm trying to achieve is the following:
owner_id | owner_username | user_1_id | user_1_username | user_2_id | user_2_username | order_total
----------------------------------------------------------------------------------------------------
1 | John | 2 | Robert | 3 | Sally | 100
So far I'm getting all I need with a query like this:
SELECT o.owner_id AS owner_id, (SELECT username FROM Users where user_id = 1) AS owner_username,
o.user_1 AS user_1_id, (SELECT username FROM Users where user_id = 2) AS user_1_username,
o.user_2 AS user_2_id, (SELECT username FROM Users where user_id = 3) AS user_2_username,
o.amount AS order_total
FROM Orders.o
WHERE o.order_id = 'AAA'
This is an example to retrieve the info for the first order.
I'm not very satisfied by the inner queries I have to do to get each username, I think it's kinda ugly.
Is there a more elegant or more performant way to get the usernames?
Thank you
This may help
SELECT od.*,
U1.username AS 'User_1_Name',
U2.username AS 'User_2_Name',
U3.username AS 'User_3_Name'
FROM Orders od
LEFT OUTER JOIN Users U1
ON od.Owner_Id = U1.User_Id
LEFT OUTER JOIN Users U2
ON od.User_1 = U2.User_Id
LEFT OUTER JOIN Users U3
ON od.User_2 = U3.User_Id
WHERE order_id = 'AAA'

Select all Users related to Company

First of all, sorry for the meaningless title. Couldn't think of anything better. Please, take a look at my following tables:
User
+-----+-------+---------------+
| id | name | email |
+-----+-------+---------------+
| 1 | NameA | namea#srv.com |
| 2 | NameB | nameb#srv.com |
| 3 | NameC | namec#srv.com |
+-----+-------+---------------+
Department
+-----+---------+-------+---------+
| id | company | name | manager |
+-----+---------+-------+---------+
| 1 | 1 | DeptA | 1 |
| 2 | 1 | DeptB | 2 |
+-----+---------+-------+---------+
Company
+-----+------+-------+
| id | name | owner |
+-----+------+-------+
| 1 | Buzz | 3 |
+-----+-------+------+
I need to find all users related to a Company. Something like:
+---------+------------+
| user_id | company_id |
+---------+------------+
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
+---------+------------+
How can I do that? I read about the different kind of joins (inner, outer, full, etc), but I couldn't figure out how to handle that "Department" table in the middle of everything.
I would do this with two queries:
select Department.manager as user_id, Company.id
from Company
join Department on Department.company=Company.id
union
select Company.owner as user_id, Company.id
from Company
select User.id, User.name, User.email, CASE WHEN User.id = Company.Owner THEN 'YES' ELSE 'NO' END as CompanyOwner
from User
inner join Department on User.DepartmentID = Department.id
inner join Company on Company.id = Department.company
I think you're missing an entitiy relationship between Users and Departments, perhaps a departmentID on the User Table. I think that might be the part you're missing.
Typically, you would have some way to link Users to the Department they work in. Either with a DeptID column added onto the User table. In this example, the owner's department is represented as null, while the department of managers is populated, but which way you do this is up to you.
User
+-----+-------+---------------+------+
| id | name | email | dept |
+-----+-------+---------------+------+
| 1 | NameA | namea#srv.com | 1 |
| 2 | NameB | nameb#srv.com | 2 |
| 3 | NameC | namec#srv.com | +++ |
| 4 | NameD | named#srv.com | 2 |
| 5 | NameE | namee#srv.com | 2 |
+-----+-------+---------------+------+
So to get the list of all company 1 users where the department is populated on the User table, this becomes simple
SELECT u.id as user_id,
d.company as company_id
FROM User u
INNER JOIN Department d ON u.dept=d.id
WHERE d.company=1
UNION
SELECT owner as user_id,
id as company_id
FROM Company
If you chose not to populate manager's department in the User table, you would have to append a UNION for that part as well, similar in construct to the first part directly above.
Or you might instead have an association table to show which employees work for which department. This might be needed if a person could work in more than one department. (The managers and owners might not need to be in this table, since they are already accounted for. Again, up to you. But if you do include them, then you wont need the UNIONs, so it will keep things simpler. You might even put the owner in an "OWNERSHIP" department.)
UserDept
+--------+--------+
| UserID | DeptID |
+--------+--------+
| 4 | 2 |
| 5 | 2 |
+--------+--------+
SELECT u.id as user_id,
d.company as company_id
FROM UserDept u
INNER JOIN Department d ON u.dept=d.id
WHERE d.company=1
(Add UNIONs on as needed, if you did not include owners / managers in the UserDept table.)
But in case you need a deeper example, let's suppose you also needed the name from the User table:
SELECT u.id as user_id,
d.company as company_id,
u.name as user_name
FROM User u
INNER JOIN UserDept x ON u.id=x.user
INNER JOIN Department d ON x.dept=d.id
WHERE d.company=1