Oracle: Using Sub-Queries, JOIN and distinct function together - sql

Here is how I contructed the step-by-step:
M1. create a sub-query that will return CustomerId and total invoiced for that customer
M2. A second subquery that will give a list of distinct ProductIDs (along with product SKUs) and CustomerIDs.
M3. The M1 and M2 subqueries will be joined to make association between customer totals and products (for the same CustomerId).
M4. The query M3 will be fed to the final query that will just find the top 5 products.
I'm stuck on creating the distinct ProductID and customerID because they would have to be in aggregate functions in order to make them distinct.
Attached is an image that is the erwin diagram which helps understand the system.
If you can help me with M1-M4, I will greatly appreciate it. I'm not a programmer by trade but a business analyst.
--M1--
select C.CustomerId, COUNT(I.InvoiceId) TotalNumInvoices
from Customer C
JOIN Invoice I ON (I.CustomerId = C.CustomerId)
group by C.CustomerId
--M2: Incomplete--
select P.ProductID, P.SKU, C.CustomerID
from Product P
JOIN InvoiceLine IL ON (IL.ProductId = P.ProductId)
JOIN Invoice I ON (IL.InvoiceId = I.InvoiceId)
JOIN Customer C ON (C.CustomerId = I.CustomerId)

you can also use the DISTINCT keyword your select clause in order to get unique values. Try this for m2:
select DISTINCT p.productID, p.sku, i.customerID
from invoice i INNER JOIN invoiceLine il
ON i.invoiceID = il.invoiceID
JOIN product p
ON il.productID = p.productID;

Related

Joining 2 Many-To-Many Relationship Tables

Problem:
Find the net balance of the total order amount and total payments for each customer.
There are 4 tables involved: OrderDetails, Orders, Payments and Customer.
The total amount order = quantity order * price each [in OrderDetails]
The total payment = sum of different payments for the same order.
Customers are linked to Payments and Orders with CustomerNumber. Orders are linked to OrderDetails with OrderNumber.
I tried joining the 4 tables with the INNER JOIN function.
SELECT
c.customername,
SUM(od.quantityordered * od.priceeach) - SUM(p.amount) AS Net_Balance
FROM
(
(
orderdetails od
INNER JOIN orders o ON od.ordernumber = o.ordernumber
)
INNER JOIN customers c ON o.customernumber = c.customernumber
)
INNER JOIN payments p ON c.customernumber = p.customernumber
GROUP BY c.customername;
The expected results should be 0 for almost every customers.
However, what I have got is the total amount order and total payment are multiplied by some constants. Specifically, the total payment shown is multiplied by the number of payments for each order.
Anybody have any ideas to save my life?
This is a typical issue when dealing with N-M relationships. To solve it, one solution is to move the aggregation to subqueries:
SELECT c.customername, o.amt - p.amt AS Net_Balance
FROM customers c
INNER JOIN (
SELECT ord.customernumber, SUM(det.quantityordered * det.priceeach) as amt
FROM orders ord
INNER JOIN orderdetails det ON ord.ordernumber = det.ordernumber
GROUP BY ord.customernumber
) o ON o.customernumber = c.customernumber
INNER JOIN (
SELECT customernumber, SUM(amount) as amt
FROM payments
GROUP BY customernumber
) p ON p.customernumber = c.customernumber
SELECT c.customername, SUM(od.quantityordered*od.priceeach) as ordersum , SUM(p.amount) as paymentsum'
What's the result of the two columns ?
Is it what you want?

Query returning individuals most recent order, date of the order, number of products in the order and the total amount

Need help with a query to returns each individuals most recent order, date of the order, number of products in the order and the total amount. I am kind of stuck trying to get the number of products and total.
Here are the table diagrams
Not sure if I should be using multiple joins or subqueries:
SELECT FirstName, LastName, MAX(O.OrderDate), O.OrderDate
FROM Customer C
INNER JOIN Order O ON C.CustomerID = O.CustomerID
It is always good practice to start from assumed dim tables such as product.This Query should help. It is better to aggregate quantity and amount to get results at aggregate level
SELECT FirstName
,LastName
,max(o.orderdate) Orderdate
,Sum(Quantity) Quantity
,Sum(TotalAmount) TotalAmount
FROM Product p
INNER JOIN Orderitem oi
ON Oi.product_id = p.id
INNER JOIN
ORDER o
ON o.id = oi.order_id
INNER JOIN Customer c
ON c.id = o.Customer_id
GROUP BY FirstName
,LastName
not sure if you want aggregations but here you go:
SELECT customer.firstname, customer.lastname, COUNT(DISTINCT orderitem.productid), [order].totalamount
FROM [order] LEFT JOIN customer ON [order].customerid=customer.id LEFT JOIN orderitem ON order.id=orderitem.orderid
WHERE [order].date=MAX([order].date)
GROUP BY customer.firstname,customer.lastname, [order].totalamount
Still don't know why you are applying a where clause for the last order, it's up to you to keep or not the where condtion.

How to query customers data with different customer levels in Postgresql?

Currently, I encounter with running query in Posgtresql server, there are 3 tables which contain customer information, customers who apply membercard and customers payment as below:
table customer
customerid, name, lastname, age
table payment
customerid, payment, product, date
table membercard
customerid, membercardno, createddate, status
In my system, there are 2 kind of customers such as regular and member customers.
Regular customers are those customers that haven't applied to be member customers yet.
Member customers are those customers that already applied to be customers.
In this scenario, I would like to query all of 2 kind customers in these 3 tables.
ex: customerid, name, lastname, age, membercardno, payment, product, date
For my query:
SELECT C.CUSTOMERID, C.NAME, C.LASTNAME, C.AGE,
M.MEMBERCARDNO, P.PAYMENT, P.PRODUCT, P.DATE
FROM CUSTOMER C,
MEMBERCARD M,
PAYMENT P
WHERE C.CUSTOMERID = M.MEMBERCARD AND
C.CUSTOMERID = P.CUSTOMERID
ORDER BY CUSTOMERID
in this case, just can query only those customers are member customers.
How to query both kind of customers? it's very hard to do for me.
Any help from our expert will be very appreciate.
You need to use an OUTER JOIN to pull in the membercard data, because not all customers have a member card.
SELECT C.CUSTOMERID, C.NAME, C.LASTNAME, C.AGE,
M.MEMBERCARDNO, P.PAYMENT, P.PRODUCT, P.DATE
FROM CUSTOMER C
LEFT OUTER JOIN MEMBERCARD M
ON M.MEMBERCARD = c.CUSTOMERID
LEFT OUTER JOIN PAYMENT P
ON P.CUSTOMERID = C.CUSTOMERID
ORDER BY C.CUSTOMERID
An OUTER JOIN is an optional join - if the data exists in the root table but doesn't exist in the joined table it will still be returned. Contrast this with an INNER JOIN (which is what you were doing, although you probably didn't realize it) where matching data must exist in both tables of the join for results to be returned.
Also, get in the habit of using ANSI-style joins as shown above, instead of the old-style implicit joins as used in your original query. It's easier to understand what's going on with ANSI joins.
Best of luck.

SQL Server Aggregation using Max() and obtaining details from max() line.

I just took a final exam and one of the questions asked me to double join three tables, and report the max sale payout for each salesperson.
The tables have the following variables:
Salesperson(id, name)
Order(orderid, order_date, Cust_id, Saleperson_id, amount)
Customer(id, name)
After joining:
select salesperson.Name, Orders.Number, customer.Name, Orders.Amount
from Orders
join salesperson
on orders.Salesperson_id = salesperson.ID
join Customer
on customer.ID = orders.cust_id
What the instructed wanted was for me to find each salesperson's maximum sell (as found by order.amount). He also wanted the salesperson (salesperson.name), the order number of the max sale (orders.number), the customer the sale was with (customer.name), and the max sale amount. What is the most efficient way to do this problem? I have tried to use "group by salesperson.name", but I cannot because the orders.number and customer.name are never held in the aggregation.
I finished the problem this way
select
salesperson.name as Sales_Person,
orders.number as Order_Number,
customer.Name as Customer_Name,
orders.Amount as Sale_Amount
from salesperson
left join Orders
on salesperson.ID = orders.Salesperson_id
left join Customer
on orders.cust_id = customer.ID
where orders.Amount in (select max(orders.Amount)
from salesperson
join Orders
on salesperson.ID = orders.Salesperson_id
join Customer
on orders.cust_id = customer.ID
group by salesperson.name)
I know this is a bad way to do it. For instance, what if two different salesperson's max sale was equivalent? Max and min are not like count and sum because it is picking out one line from a aggregation, but the rules still apply. Also, you might notices that there is no real unique identifier in the joined table other than order.number which is not useful. Therefore, I would have to use some composite of salesperson.name and order.number.
Also, what do I do if I have to pick the top three sales for each salesperson? Should such an output be totally different code-wise than what would be required from just the first sale?
I keep bumping me head against this problem, and I would love to have a more professional approach to this problem.
SELECT
M.max_amount,
S.Name,
O.Number,
C.Name
FROM orders O
JOIN salesperson S
ON S.Salesperson_id = O.Salesperson_id
JOIN customer C
ON C.Customer_id = O.Customer_id
JOIN (
SELECT Salesperson_id, MAX(amount) max_amount
FROM Order
GROUP BY Salesperson_id
) M
ON M.Salesperson_id = O.Salesperson_id AND M.max_amount = O.amount
For the top 3:
SELECT
M.Amount,
S.Name,
O.Number,
C.Name
FROM orders O
JOIN salesperson S
ON S.Salesperson_id = O.Salesperson_id
JOIN customer C
ON C.Customer_id = O.Customer_id
CROSS APPLY (
SELECT TOP 3 Amount
FROM Order
WHERE Salesperson_id = O.Salesperson_id
ORDER BY Amount DESC
) M

Fetch data from more than one tables using Group By

I am using three tables in a PostgreSql database as:
Customer(Id, Name, City),
Product(Id, Name, Price),
Orders(Customer_Id, Product_Id, Date)
and I want to execute a query to get from them "the customers that have have ordered at least two different products alnong with the products". The query I write is:
select c.*, p.*
from customer c
join orders o on o.customer_id = c.id
join product p on p.id = o.product_id
group by (c.id)
having count(distinct o.product_id)>=2
It throws the error:
"column "p.id" must appear in the GROUP BY clause or be used in an aggregate function
LINE 1: select c.*, p.*".
However if I remove the the p.* from select statement (assuming that I one does not want the products, only the customers), it runs fine. How can I get the products as well?
Update: Having ordered two or more products, a customer must appear on the output as many times as its product he has ordered. I want as output a table with 5 columns:
Cust ID | Cust Name | Cust City | Prod ID | Prod Name | Prod Price
Is it possible in SQL given that group by should be used? Shoul it be used on more than one columns on different tables?
Try this out :
SELECT distinct c.* ,p.*
FROM Customer c
JOIN
(SELECT o.customer_id cid
FROM Product P
JOIN Orders o
ON p.id= o.product_id
GROUP BY o.customer_id
HAVING COUNT(distinct o.product_id)>=2) cp
ON c.id =cp.cid
JOIN Orders o
on c.id=o.customer_id
JOIN Product p
ON o.product_id =p.id
I hope it solves your problem.
I think you can use following query for this question -
SELECT C1.*, p1.*
FROM Customer C1
JOIN Orders O1 ON O1.Customer_Id = C1.Id
JOIN Product P1 ON P1.Id = O1.Product_Id
WHERE C1.Id IN (SELECT c.Id
FROM Customer c
JOIN Orders o ON o.Customer_Id = c.Id
GROUP BY (c.Id)
HAVING COUNT(DISTINCT o.Product_Id) >= 2)