Combining two queries using JOIN, GROUP BY and SUM? - sql

I am stuck trying to write the correct query for this problem. So I have 2 tables orders and products where orders.user_id=products.buyer_id
I want to query from both tables and find out how much each person owes for their purchase, how much they actually paid, and finally the difference between the two (owes-paid).
The individual queries that work are
SELECT buyer_id, SUM(price) AS owes FROM products GROUP BY buyer_id ORDER BY buyer_id ASC;
and
SELECT user_id, SUM(amount_paid) AS paid FROM orders GROUP BY user_id ORDER BY user_id ASC;
I am able to do the right query but only on each table individually. However, when trying to combine both queries (Outer Join?), I get bad results.
Any help/guidance is appreciated.

I would suggest a sub query where you take the union of both, and dedicate a column to the paid amount and another to the due amount. Then apply the aggregation on that sub query:
SELECT user_id,
SUM(amount_due) AS owes,
SUM(amount_paid) AS paid,
SUM(amount_due) - SUM(amount_paid) AS diff
FROM (
SELECT user_id, amount_paid, 0 amount_due
FROM orders
UNION ALL
SELECT buyer_id, 0, price
FROM products
) AS transactions
GROUP BY user_id
ORDER BY user_id ASC;

Related

Best approach to display all the users who have more than 1 purchases in a month in SQL

I have two tables in an Oracle Database, one of which is all the purchases done by all the customers over many years (purchase_logs). It has a unique purchase_id that is paired with a customer_id.The other table contains the user info of all the customers. Both have a common key of customer_id.
I want to display the user info of customers who have more than 1 unique item (NOT the item quantity) purchased in any month (i.e if A customer bought 4 unique items in february 2020 they would be valid as well as someone who bought 2 items in june). I was wondering what should my correct approach be and also how to correct execute that approach.
The two approaches that I can see are
Approach 1
Count the overall number of purchases done by all customers, filter the ones that are greater than 1 and then check if they any of them were done within a month.
Use this as a subquery in the where clause of the main query for retrieving the customer info for all the customer_id which match this condition.
This is what i've done so far,this retrieves the customer ids of all the customers who have more than 1 purchases in total. But I do not understand how to filter out all the purchases that did not occur in a single arbitrary month.
SELECT * FROM customer_details
WHERE customer_id IN (
SELECT cust_id from purchase_logs
group by cust_id
having count(*) >= 2);
Approach 2
Create a temporary table to Count the number of monthly purchases of a specific user_id then find the MAX() of the whole table and check if that MAX value is bigger than 1 or not. Then if it is provide it as true for the main query's where clause for the customer_info.
Approach 2 feels like the more logical option but I cannot seem to understand how to write the proper subquery for it as the command MAX(COUNT(customer_id)) from purchase_logs does not seem to be a valid query.
This is the DDL diagram.
This is the Sample Data of Purchase_logs
Customer_info
and Item_info
and the expected output for this sample data would be
It is certainly possible that there is a simpler approach that I am not seeing right now.
Would appreciate any suggestions and tips on this.
You need this query:
SELECT DISTINCT cust_id
FROM purchase_logs
GROUP BY cust_id, TO_CHAR(purchase_date, 'YYYY-MON')
HAVING COUNT(DISTINCT item_id) > 1;
to get all the cust_ids of the customers who have more than 1 unique item purchased in any month and you can use with the operator IN:
SELECT *
FROM customer_details
WHERE customer_id IN (
SELECT DISTINCT cust_id -- here DISTINCT may be removed as it does not make any difference when the result is used with IN
FROM purchase_logs
GROUP BY cust_id, TO_CHAR(purchase_date, 'YYYY-MON')
HAVING COUNT(DISTINCT item_id) > 1
);
One approach might be to try
with multiplepurchase as (
select customer_id,month(purchasedate),count(*) as order_count
from purchase_logs
group by customer_id,month(purchasedate)
having count(*)>=2)
select customer_id,username,usercategory
from mutiplepurchase a
left join userinfo b
on a.customer_id=b.customer_id
Expanding on #MT0 answer:
SELECT *
FROM customer_details CD
WHERE exists (
SELECT cust_id
FROM purchase_logs PL
where CD.customer_id = PL.customer_id
GROUP BY cust_id, item_id, to_char(purchase_date,'YYYYMM')
HAVING count(*) >= 2
);
I want to display the user info of customers who have more than 1 purchases in a single arbitrary month.
Just add a WHERE filter to your sub-query.
So assuming that you wanted the month of July 2021 and you had a purchase_date column (with a DATE or TIMESTAMP data type) in your purchase_logs table then you can use:
SELECT *
FROM customer_details
WHERE customer_id IN (
SELECT cust_id
FROM purchase_logs
WHERE DATE '2021-07-01' <= purchase_date
AND purchase_date < DATE '2021-08-01'
GROUP BY cust_id
HAVING count(*) >= 2
);
If you want the users where they have bought two-or-more items in any single calendar month then:
SELECT *
FROM customer_details c
WHERE EXISTS (
SELECT 1
FROM purchase_logs p
WHERE c.customer_id = p.cust_id
GROUP BY cust_id, TRUNC(purchase_date, 'MM')
HAVING count(*) >= 2
);

How to get the customer info of the customer with the highest number of transactions

I have an oracle sql database consisting of three tables and I was wondering,
What is the most efficient subquery that can be written to retrieve the information of the customer stored in the table customer_info who has performed the highest amount of purchases in total.(The purchase data is in the table purchase_logs). i.e the number of transactions one customer has performed NOT the quantity of the items purchased.
i.e my aim is to retrieve the customer details of the customer witht he highest amount of purchases done.
I have 3 tables one for the customer_info, one as the purchase_logs and the last one being the item_info.
My current Approach
SELECT * FROM customer_info
WHERE customer_id = (SELECT cust_id
FROM purchase_logs
GROUP BY cust_id
ORDER BY COUNT(*)
DESC LIMIT 1);
This doesn't seem to give me any results at all unfortunately.
This is my Database Schema along with the Sample Data of purchase_logs, customer_info, item_info and the Expected Output
I would really appreciate any help in understanding what the proper approach to solving this problem would be.
There is no limit 1 in Oracle SQL, use row limiting clause instead (fetch first in the example below):
SELECT *
FROM
(SELECT cust_id, count(*) cnt
FROM purchase_logs
GROUP BY cust_id
ORDER BY cnt desc
fetch first 1 row only with ties
) vc
join customer_info
on customer_id = vc.cust_id;

Multiple Aggregation in SQL

loan_no loan_amt contact date customer_id salesman_id
I have the following table. I need to somehow get the average of loan_no and the average of loan_amt for the people with more than one loan_no. I need to somehow plug in the avg and count functions.
I am seriously struggling with that. I was also thinking of a pivot function.
I would really appreciate it if someone can suggest a SQL code
My efforts so far:
select count (loan_no), tcustomer_id
from table
group by customer_id
having count (loan_no) > 1
Now I just do not know how to also include the avg function.
Not sure why do you need average Loan_no but you can still get it through -
select customer_id, avg(loan_no), avg(loan_amt)
from (select *, count(*) over(partition by customer_id) cnt
from table)
where cnt > 1
group by customer_id
You can use two levels of aggregation:
select avg(num_loans), sum(total) / sum(num_loans)
from (select customer_id, count(*) as num_loans, sum(loan_amt) as total
from table
group by customer_id
) t
where num_loans > 1;

Select all the columns from a table but group it with only few

May be this is a basic question but I’m unable solve this by myself, it will be greatly appreciated if someone can help me in achieving the following:
I want to get all the columns from a table but group it with only few. My query is as follows:
SELECT NATIONAL_ID,
EMPLID,
PLAN,
CHECK_DT,
PERIODS,
SUM (SALARY),
SUM (EE_CONT),
SUM (ER_CONT),
SUM (AL_HOURS)
FROM DTL_TMP
WHERE EMPLID = 'xxxxx'
GROUP BY NATIONAL_ID,
EMPLID,
PLAN,
CHECK_DT,
PERIODS;
If I add more columns to the above query it returns all the rows but I want only rows that are returned by the above query.
I did try few other options/examples like below:
SELECT OD.ProductID, OD.ProductName, CalQ.OrderQuantity
FROM (SELECT DISTINCT ProductID, ProductName
FROM OrderDetails) OD
INNER JOIN (SELECT ProductID, OrderQuantity SUM(OrderQuantity)
FROM OrderDetails
GROUP BY ProductID) CalQ
ON CalQ.ProductID = OD.ProductID
But it retrieves multiple rows because of the non-existence of keys in the table. Now, if I make any field as a key it gives unique constraint violation.
Please help.
Thanks in advance!

How can I rewrite this query without sub-queries?

So what I want to do:
I have a table that has some prices of some items of a store, what I want to do is to find the average price of all the items sold from that store, AND the sum of all similar items sold in store.
My columns are:
item_no
branch
totalamount
What is really important is that I have to avoid sub-queries so I couldn't do something like:
SELECT DISTINCT branch AS postcode, item_no, avg_price
FROM Prices
NATURAL JOIN (SELECT branch, AVG(totalamount) avg_price FROM Prices GROUP BY branch) av
WHERE sum > avg_price ORDER BY turnover DESC , branch;
Which does exactly what I want to do, nevertheless I have to do it without sub-queries.
edit 1 QUESTION:
IS THERE A DIFFERENCE between derived and temporary tables? so for the assignment , i am not allowed to use subqueries, OR temporary tables, so does my answer have any of those 2?
You can specify multiple aggregate statements on the same or different columns within the same SELECT statement. To see exactly what I mean have a look in books online.
http://msdn.microsoft.com/en-us/library/ms177677.aspx
here how you can do it,
SELECT branch AS postcode,
item_no,
AVG(totalamount) avg_price ,
SUM(totalamount) sum
FROM prices
WHERE SUM(totalamount) > avg_turnover
ORDER BY avg_turnover DESC ,
eatit_Order.branch
GROUP BY branch,
item_no;