join 2 foreign key using subquery - sql

help me solve this, i am intended to join 2 table for 2 different foreign key within the same column, table snapshot provide below:
users table
transactions table
i want to return top 5 based on transactions amount from high-low alongside to display transactions id, investor id, investor name, borrower id, borrower name, amount
the following run properly but contains no investor name
select top 5 t.id,
investor_id,
borrower_id,
username as BorrowerName,
amount
from transactions t join users u on t.borrower_id = u.id
order by t.amount desc;
minus investor name result table
while if i do subquery resulting error
select top 5 t.id,
investor_id,
(select username from users join transactions on users.id =
transactions.investor_id) investorName,
borrower_id,
username BorrowerName,
amount
from transactions t join users u on t.borrower_id = u.id
order by t.amount desc;

select top 5 t.id,
investor_id, ui.username as InvestorName,
borrower_id, ub.username as BorrowerName,
amount
from transactions t
join users ub on t.borrower_id = ub.id
join users ui on t.investor_id = ui.id
order by t.amount desc;

The Subquery must be scalar. i.e. return a single value, but you currently return a result set.
select top 5 t.id,
investor_id,
(-- Correlated Scalar Subquery, returns a single value
select username
from users
WHERE users.id = transactions.investor_id) investorName,
borrower_id,
username BorrowerName,
amount
from transactions t join users u on t.borrower_id = u.id
order by t.amount desc;

Isn't this what you want? Two joins on users table
SELECT TOP 5
investor_id,
investors.username InvestorName,
borrower_id,
borrowers.username BorrowerName,
amount
FROM
transactions
INNER JOIN users investors ON (transactions.investor_id = investors.id)
INNER JOIN users borrowers ON (transactions.borrower_id = borrowers.id)
ORDER BY
amount desc;
I would recommend against using subqueries in this case, since the database will be forced to perform two sequential scans in a nested loop for each row.

Related

Get active and total bookings (from 1 table) for every user in users table

I have 2 tables:
users:
username (pk)
bookings:
username (fk)
status (A = Active, C = Cancelled , D = DONE)
I'm willing to show user details along with with their count of active and total bookings (where total bookings will be all the entries in "bookings" table for a particular user).
Table to show:
username, active bookings (count), total bookings (count)
Currently I'm unable to make an efficient query for this.
My DB is postgresql.
Please assist.
Thank you
As you are using PostgreSQL you can take the advantage of Filter() clause. Also you have to use Left Join because you want the details for every user from user table. So Write your query like below:
select
t1.username,
count(*) filter (where t2.status='A') as "Active_Bookings",
count(t2.*) as "Total_Bookings"
from users t1 left join bookings t2 on t1.username=t2.username
group by 1
Edit as per comment:
Filter clause is supported by Postgresql and SQLite. For others count with case will do the thing. Below query should work for almost every other database.
select
t1.username,
count(case when t2.status='A' then 1 end) as "Active_Bookings",
count(t2.*) as "Total_Bookings"
from users t1 left join bookings t2 on t1.username=t2.username
group by t1.username
you can use sum(case when t2.status='A' then 1 else 0 end) as "Active_Bookings" also.
You can try the below -
select u.username,count(*) as total_booking,
count(case when status='Active' then 1 end) as active_bookings
from users u join bookings b on u.username=b.username
group by u.username
Not very sure I udnerstood your question but based on the input you gave try this , this should work
select x.username,active_bookings,total_bookings from (
(select username, count(status) as active_bookings from bookings where status='A' group by username)x join (select username,count(status) as total_bookings from bookings group by username)y on x.username=y.username);

SQL: Write a query to get the average order value by gender

I have two tables: one represents transaction and the other represents customer attributes. "transactions" table has 4 columns: id, user_id, product_id, quantity. "users" table has 3 columns: id, name, sex.
The goal is to write a query to find the average quantity value by gender. I can't quiet get this one. I guess we have to use groupby for gender, and avg() for quantity? I'm new to SQL and I'm not sure how to use these. Thank you!
Try this query:
SELECT AVG(t.quantity), u.sex
FROM TRANSACTIONS t
INNER JOIN USERS u ON t.user_id = u.id
GROUP BY u.sex
I think below should work
select avg(price), u.sex
from products
join transaction as t
on products.id = t.product_id
join users as u
on t.user_id = u.id
group by u.sex

Best approach for limiting rows coming back in SQL when joining for a sum

I need to get back a list of users and the total amount that they have ordered. In reality my query is more complex but I think this sums it up. My issue is, if a user made 5 orders for example, I'll get back their name and the total they've ordered 5 times due to the join (having 5 rows in the order table for that user).
What's the recommended approach for when you need to total the records in one table that has multiple rows without requiring many rows to come back? distinct could work but is this the best? (especially when my select chooses more information than what's below)
SELECT user.name, sum(order.amount) FROM USER user
INNER JOIN USER_ORDERS order
ON (user.user_id = order.user_id)
Are you just looking for GROUP BY?
SELECT u.name, SUM(o.amount)
FROM USER u JOIN
USER_ORDERS uo
ON u.user_id = uo.user_id
GROUP BY u.name, u.user_id;
Note that this has included user_id in the GROUP BY, just in case two users have the same name.
If you want all users, even those without orders, then you want a LEFT JOIN:
SELECT u.name, SUM(o.amount)
FROM USER u LEFT JOIN
USER_ORDERS uo
ON u.user_id = uo.user_id
GROUP BY u.name, u.user_id;
Or a correlated subquery:
SELECT u.name,
(SELECT SUM(o.amount)
FROM USER_ORDERS uo
WHERE u.user_id = uo.user_id
)
FROM USER u;
You could use the analytic version of SUM.
SELECT u.name, SUM(o.amount) OVER(PARTITION BY u.name)
FROM USER u JOIN
USER_ORDERS uo
ON u.user_id = uo.user_id;

Too much Data using DISTINCT MAX

I want to see the last activity each individual handset and the user that used that handset. I have a table UserSessions that stores the last activity of a particular user as well as what handset they used in that activity. There are roughly 40 handsets, yet I always get back way too many records, like 10,000 rows when I only want the last activity of each handset. What am I doing wrong?
SELECT DISTINCT MAX(UserSessions.LastActivity), Handsets.Name,Users.Username
FROM UserSessions
INNER JOIN Handsets on Handsets.HandsetId = UserSessions.HandsetId
INNER JOIN Users on Users.UserId = UserSessions.UserId
WHERE
Handsets.Name in (1000,1001.1002,1003,1004....)
AND Handsets.Deleted = 0
GROUP BY UserSessions.LastActivity, Handsets.Name,Users.Username
I expect to get one record per handset of the users last activity with that handset. What I get is multiple records on all handsets and dates over 10000 rows
You typically GROUP BY the same columns as you SELECT, except those who are arguments to set functions.
This GROUP BY returns no duplicates, so SELECT DISTINCT isn't needed.
SELECT MAX(UserSessions.LastActivity), Handsets.Name, Users.Username
FROM UserSessions
INNER JOIN Handsets on Handsets.HandsetId = UserSessions.HandsetId
INNER JOIN Users on Users.UserId = UserSessions.UserId
WHERE Handsets.Name in (1000,1001.1002,1003,1004....)
AND Handsets.Deleted = 0
GROUP BY Handsets.Name, Users.Username
There is no such thing as DISTINCT MAX. You have SELECT DISTINCT which ensures that all columns referenced in the SELECT are not duplicated (as a group) across multiple rows. And there is MAX() an aggregation function.
As a note: SELECT DISTINCT is almost never appropriate with GROUP BY.
You seem to want:
SELECT *
FROM (SELECT h.Name, u.Username, MAX(us.LastActivity) as last_activity,
RANK() OVER (PARTITION BY h.Name ORDER BY MAX(us.LastActivity) desc) as seqnum
FROM UserSessions us JOIN
Handsets h
ON h.HandsetId = us.HandsetId INNER JOIN
Users u
ON u.UserId = us.UserId
WHERE h.Name in (1000,1001.1002,1003,1004....) AND
h.Deleted = 0
GROUP BY h.Name, u.Username
) h
WHERE seqnum = 1

SQL: Two queries in a single set of results?

I'm using Postgres 9.6. I have three tables, like this:
Table public.user
id integer
name character varying
email character varying
Table public.project
id integer
user_id integer
Table public.sale
id integer
user_id integer
user_id is a foreign key in both the project and sale tables.
Is there a way I can get a list back of all user IDs with the number of projects and number of sales attached to them, as a single query?
So I'd like final data that looks like this:
user_id,num_projects,num_stories
121,28,1
122,43,6
123,67,2
I know how to do just the number of projects:
SELECT "user".id, COUNT(*) AS num_visualisations
JOIN project ON project.user_id="user".id
GROUP BY "user".id
ORDER BY "user".id DESC
But I don't know how also to get the number of sales too, in a single query.
Use subqueries for the aggregation and a left join:
select u.*, p.num_projects, s.num_sales
from user u left join
(select p.user_id, count(*) as num_projects
from projects p
group by p.user_id
) p
on p.user_id = u.id left join
(select s.user_id, count(*) as num_sales
from sales s
group by s.user_id
) s
on s.user_id = u.id;