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

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'

Related

SQL check if a record has a reference from another table and if so do something

I have two tables, users and friendships
id | name | password
-------------------------
1 | Dave | 1234
-------------------------
2 | John | abcd
-------------------------
3 | Bob | xyz
-------------------------
and the friendships
friend_one | friend_two | status
-------------------------------------
1 | 1 | me
-------------------------------------
2 | 2 | me
-------------------------------------
3 | 3 | me
------------------------------------
1 | 2 | pending
-------------------------------------
3 | 1 | active
Now whenever a user logged in to the system, I need to fetch all the users in the database, also I need to add an extra column to the result from the select query, which shows the friendship status with currently logged in user.
For example, user id 1 , Dave logged in to the system and request for users list, select query should be output as below,
id | name | password | friend_status
------------------------------------------
1 | Dave | 1234 | me
------------------------------------------
2 | John | abcd | pending
------------------------------------------
3 | Bob | xyz | active
------------------------------------------
When the user id 2, John logged in to the system, he need to get the below table,
id | name | password | friend_status
------------------------------------------
1 | Dave | 1234 | pending
------------------------------------------
2 | John | abcd | me
------------------------------------------
3 | Bob | xyz | null
------------------------------------------
Is this even possible ? I tried to join tables and use a nested query
For example, with the below query I can get all the friendships for the currently logged in user,
select * from friendships
where friend_one = 2 or friend_two = 2
But I don't understand what to do next. I tried to put this as a nested query of the main select query and tried to join tables but I'm not getting what I wanted.
Can anyone give me an idea?
You need to CROSS JOIN the list of id values from users with the users table to get all distinct pairs of id values, then LEFT JOIN that to the friendships table to get the friend status for each user:
SELECT u1.id, u.name, u.password,
f.status
FROM users u
CROSS JOIN (SELECT DISTINCT id
FROM users) u1
LEFT JOIN friendships f ON f.friend_one = u.id AND f.friend_two = u1.id
OR f.friend_one = u1.id AND f.friend_two = u.id
ORDER BY u1.id, u.id
Output:
id name password status
1 Dave 1234 me
1 John abcd pending
1 Bob xyz active
2 Dave 1234 pending
2 John abcd me
2 Bob xyz (null)
3 Dave 1234 active
3 John abcd (null)
3 Bob xyz me
Demo on SQLFiddle
To get a specific user, just add WHERE u1.id = ? before the ORDER BY clause.

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

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

PostgreSQL join two tables with LIMIT 1

I have two tables:
First table "persons"
id | name |
---------------
1 | peter |
3 | martin |
5 | lucy |
Second table "meetings"
id | date | id_persons |
--------------------------------
1 | 2014-12-08 | 1 |
2 | 2013-05-10 | 2 |
3 | 2015-08-25 | 1 |
4 | 2016-10-18 | 1 |
5 | 2012-01-01 | 3 |
6 | 2016-09-28 | 5 |
I need somehow get only last date from "meeting" table for every person (or selected). And result table must be order by name. I thought, it could be like this, but WHERE clause in LEFT JOIN can't be used:
SELECT meetings.id, meetings.date, persons.name FROM persons
LEFT JOIN (SELECT meetings.date, meetings.id, meetings.id_persons FROM
meetings WHERE persons.id = meetings.id_persons ORDER BY
meetings.date DESC LIMIT 1) m ON m.id_persons = persons.id
WHERE persons.id < 6 ORDER BY persons.name
So I started with DISTINCT and it worked, but I think that it is not good idea:
SELECT * FROM
(SELECT DISTINCT ON (persons.id) persons.id, persons.name,
m.date, m.id FROM persons
LEFT JOIN (SELECT meetings.id, meetings.date, meetings.id_persons
FROM meetings ORDER BY meetings.date DESC) m
ON m.id_persons = persons.id
WHERE persons.id < 6 ORDER BY persons.id) p
ORDER BY p.name
Result what I need is:
name | date | id_meetings
-----------------------------------
lucy | 2016-09-28 | 6
martin | 2012-01-01 | 5
peter | 2016-10-18 | 4
Could you help me with better solution?
In Postgres, the easiest way is probably distinct on:
select distinct on (p.id) p.*, m.*
from persons p left join
meetings m
on m.id_persons = p.id
order by p.id, m.date desc;
Note: distinct on is specific to Postgres.

display basic information from tables

I have two tables i.e.
Users
uid | firstname
1 | John
2 | Bob
3 | Paul
4 | Peter
Calls
cid | assigned_to | caller_id
1 | 2 | 1
2 | 1 | 3
3 | 2 | 4
4 | 4 | 2
assigned_to and caller_id are just the uid in users.
I just want to display the results of each call:
call_id | username(assigned_to) | username(caller_id)
How can I do this in SQL?
Thanks,
Try this:
select
cid as call_id,
A.username, -- assingned to
B.username -- caller id
from calls
left join users A on calls.assigned_to = A.uid
left join users B on calls.caller_id = B.uid