Different User, friendships and selection - sql

I have two tables:
User:
+----+----------+--+
| | User | |
+----+----------+--+
| pk | email | |
| | password | |
| | ... | |
+----+----------+--+
and Friendship:
+----+-------------+--+
| | FriendShip | |
+----+-------------+--+
| pk | user1_email | |
| pk | user2_email | |
| | date | |
| | accepted | |
+----+-------------+--+
So basically, when someone wants to befriend someone else, we record it in the friendship table.
Let's say now that we want to select all the user with less than 3 friends, it is easy to that except for people who does not have friends because there is no record of that in the friendship table.
My query looks like this for the moment:
SELECT u.email, COUNT(u.email)
FROM user u
INNER JOIN friendship f ON f.user1_email = u.email OR f.user2_email = u.email
GROUP BY u.email
HAVING COUNT(u.email) < 3;
How can I add the result of the asocial people :p?

You could use a UNION to quickly get what you want.
SELECT u.email, COUNT(u.email)
FROM user u
INNER JOIN friendship f ON f.user1_email = u.email OR f.user2_email = u.email
GROUP BY u.email
HAVING COUNT(u.email) < 3
UNION
SELECT u.email, 0
FROM user u
WHERE u.email not in (SELECT user1_email from friendship)
AND u.email not in (SELECT user2_email from friendship);

Related

SQL - Finding unique values on two tables

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

tsql join tables with 2 ids

I have this tables (Users and Meetings):
UserId | Name
1 | John
2 | Linda
3 | David
and
UserId_1 | UserId_2 | MeetingDate
1 | 2 | 15/01/2018
3 | 2 | 17/01/2018
1 | 3 | 19/01/2018
How do I select from the 2 tables in order to get in each row the Name associated to each id? I'd like to get something like this:
UserId_1 | UserId_2 | User_1_Name | User_2_Name | MeetingDate
1 | 2 | John | Linda | 15/01/2018
3 | 2 | David | Linda | 17/01/2018
1 | 3 | John | David | 19/01/2018
Thank you.
You just need two inner join:
SELECT m.UserId_1
,m.UserId_2
,u1.NAME
,u2.NAME
,m.MeetingDate
FROM Meetings m
INNER JOIN Users u1 ON u1.UserId = m.UserId_1
INNER JOIN Users u2 ON u2.UserId = m.UserId_2
You just need to alias one or both references to the table which you need to join twice...
SELECT
user1.UserId AS UserId_1,
user2.UserId AS UserId_2,
user1.Name AS User_1_Name,
user2.Name AS User_2_Name,
Meetings.MeetingDate
FROM
Meetings
INNER JOIN
Users AS user1
ON user1.UserId = Meetings.UserId_1
INNER JOIN
Users AS user2
ON user2.UserId = Meetings.UserId_2
The alias means that you can reference which instance of Users you're referring to without any ambiguity.
select Meetings.UserId_1,
Meetings.UserId_2,
isnull(a.Name,'--Unknown--') User_1_Name,
isnull(b.Name,'--Unknown--') User_2_Name,
MeetingDate
from Meetings left join Users a on Meetings.UserId_1 = a.UserID
left join Users b on Meetings.UserId_2 = b.UserID
Another approach is
SELECT m.UserId_1
,m.UserId_2
,(select name from Users where Users.id = m.UserId_1) as User_1_Name
,(select name from Users where Users.id = m.UserId_2) as User_2_Name
,m.MeetingDate
FROM Meetings m
Regards
Abdul

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 |
+--------+--------------+--------------+

Conditional testing in Oracle SQL

I am trying to write query on a tables which query record based on some condition plus I need one column as status which should checking matching record on another table and set value based on that test.
For example, we have these two tables:
| USER_ID | FIRST_NAME | LAST_NAME | CURR_STATUS |
--------------------------------------------------
| 1234567 | ABC | DEF | ACTIVE |
| 8910111 | GHI | JKL | INACTIVE |
| 2131415 | MNO | PQR | INACTIVE |
| 1617181 | STU | VWX | ACTIVE |
| 9202122 | YZA | BCD | ACTIVE |
--------------------------------------------------
Table: USER
| USER_ID | DOC_NAME |
---------------------
| 1234567 | EFG.TXT |
| 1617181 | HIJ.PDF |
----------------------
Table: USER_DOC
> select first_name, last_name, curr_status
//TODO some sql code for HAS_DOC logic
from USER
where CURR_STATUS = 'ACTIVE';
| FIRST_NAME | LAST_NAME | CURR_STATUS | HAS_DOC |
--------------------------------------------------
| ABC | DEF | ACTIVE | YES |
| STU | VWX | ACTIVE | YES |
| YZA | BCD | ACTIVE | NO |
--------------------------------------------------
Result
Right now I am doing this in Java code with two separate SQL calls:
First to get the all record matching record from USER table.
Then calling second query in the for loop
select user_id from user_doc from user_id = :userId;
I am trying to see if it is can be done with single SQL query and it is faster.
Any suggestion?
One possible solution is to use the EXISTS operator:
SELECT u.first_name, u.last_name, u.curr_status
FROM user u
WHERE u.curr_status = 'ACTIVE'
AND EXISTS (SELECT d.doc_name FROM user_doc d WHERE d.user_id = u.user_id);
If you need more information (say, a document count), you may also try
SELECT u.first_name, u.last_name, u.curr_status, COUNT(d.doc_name) AS num_documents
FROM user u LEFT JOIN user_doc d ON d.user_id = u.user_id
WHERE u.curr_status = 'ACTIVE'
GROUP BY u.first_name, u.last_name, u.curr_status
HAVING num_documents > 0;
One last alternative would be to use the IN operator
SELECT u.first_name, u.last_name, u.curr_status
FROM user u
WHERE u.curr_status = 'ACTIVE'
AND u.user_id IN (SELECT d.user_id FROM user_doc d);
EDIT The "HAS_DOC" attribute can be derived from the count, for example
-- Gets all users
SELECT u.first_name, u.last_name, u.curr_status, COUNT(d.doc_name) AS num_documents
FROM user u LEFT JOIN user_doc d ON d.user_id = u.user_id
WHERE u.curr_status = 'ACTIVE'
GROUP BY u.first_name, u.last_name, u.curr_status;
and in your Java code:
boolean hasDoc = resultSet.getInt("num_documents") > 0;
I recommend something like this. (EDIT: tested, and it works)
select u1.first_name, u1.last_name, u1.curr_status,
nvl2(u2.user_id, 'YES', 'NO') as has_doc
from USER u1
left join (select u.user_id
from USER u
where u.CURR_STATUS = 'ACTIVE'
and exists (
select null
from user_doc ud
where ud.user_id = u.user_id)
) u2 ON u1.user_id = u2.user_id
where u1.CURR_STATUS = 'ACTIVE';

Include admin role in users table from roles table

Is there a way to query users table like this:
| id | username |
-----------------
| 1 | user1 |
| 2 | user2 |
| 3 | user3 |
and user_roles table:
| id_user | id_role |
---------------------
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 2 | 2 |
| 3 | 1 |
assuming that role with id = 1 is an admin role, to produce result that looks like this:
| id | username | admin |
-------------------------
| 1 | user1 | Y |
| 2 | user2 | N |
| 3 | user3 | Y |
I think it can be done using nested SELECT statements, but I was wondering if it's doable using JOIN.
Edit:
The admin column value doesn't have to be Y or N, it can be admin role id (1) or NULL or whatever that will let me know if user is an admin
I would try this:
select u.id, u.username, if (id_role is null, 'N', 'Y') as is_admin
from users u
left outer join user_roles r
on u.id = r.id_user and r.id_role = 1
But I'm not 100% sure.
Well, you can join like this, which will give you the id_role in the result.
SELECT u.*, r.id_role
FROM users u
LEFT JOIN user_roles r
ON u.id=r.id_user
You can add WHERE r.id_role=1 to get just the admins, etc.
But to get the admin as "Y" or "N" as you wanted, you can use an IF on whether the id_role is 1 or not.
SELECT u.*, IF(r.id_role = 1, "Y", "N") as admin
FROM users u
LEFT JOIN user_roles r
ON u.id=r.id_user
select u.id_user, if(count(1) > 0, 'Y', 'N')
from user u left outer join user_roles ur on u.user_id = ur.user_roles
where ur.id_role=1
group by u.id_user
I'm not using mysql, so just googled it has the 'if' function, if that's wrong, replace with DECODE, COALESCE or alike.