SQL EXISTS returns all rows, more than two tables - sql

I know similar questions like this have been asked before, but I have not seen one for more than 2 tables. And there seems to be a difference.
I have three tables from which I need fields, customers where I need customerID and orderID from, orders from which I get customerID and orderID and lineitems from which I get orderID and quantity (= quantity ordered).
I want to find out how many customers bought more than 2 of the same item, so basically quantity > 2 with:
SELECT COUNT(DISTINCT custID)
FROM customers
WHERE EXISTS(
SELECT *
FROM customers C, orders O, lineitems L
WHERE C.custID = O.custID AND O.orderID = L.orderID AND L.quantity > 2
);
I do not understand why it is returning me the count of all rows. I am correlating the subqueries before checking the >2 condition, am I not?
I am a beginner at SQL, so I'd be thankful if you could explain it to me fundamentally, if necessary. Thanks.

You don't have to repeat customers table in the EXISTS subquery. This is the idea of correlation: use the table of the outer query in order to correlate.
SELECT COUNT(DISTINCT custID)
FROM customers c
WHERE EXISTS(
SELECT *
FROM orders O
JOIN lineitems L ON O.orderID = L.orderID
WHERE C.custID = O.custID AND L.quantity > 2
);

I would approach this as two aggregations:
select count(distinct customerid)
from (select o.customerid, l.itemid, count(*) as cnt
from lineitems li join
orders o
on o.orderID = l.orderId
group by o.customerid, l.itemid
) ol
where cnt >= 2;
The inner query counts the number of items that each customer has purchased. The outer counts the number of customers.
EDIT:
I may have misunderstood the question for the above answer. If you just want where quantity >= 2, then that is much easier:
select count(distinct o.customerid)
from lineitems li join
orders o
on o.orderID = l.orderId
where l.quantity >= 2;
This is probably the simplest way to express the query.

I suggest you to use "joins" ,
Try this
select
count(*)
From
orders o
inner join
lineitems l
on
l.orderID = o.orderID
where
l.quantity > 2

Related

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.

sql use aggregate function that counts a unique value with group by using inner joins

I searched and found similar questions online but not my particular one, they all use where or having clause.If theres one similar to mine please link it. It's a 2 part question and I have the first one done. Thank you in advance.
Okay so heres the question, part 1
"Find by customer, the total cost and the total discounted cost for each product on the order ?".
It also asks to use inner joins to find the customer and order it a specific way. Below is the answer.
SELECT
C.companyname, O.orderid, O.orderdate, P.productname,
OD.orderid, OD.unitprice, OD.qty, OD.discount,
(OD.unitprice * OD.qty - (OD.qty * OD.discount)) AS TotalCost,
(OD.qty * OD.discount) AS TotalDiscountedCost
FROM
Sales.Customers AS C
INNER JOIN
Sales.Orders AS O ON C.custid = O.custid
INNER JOIN
Sales.OrderDetails OD ON O.orderid = OD.orderid
INNER JOIN
Production.Products as P ON OD.productid = P.productid
ORDER BY
C.companyname, O.orderdate;
Now the second question is to
follow up and resume the first one by "customer and the order date year, the total cost and the total discounted cost on the order ?". It also asks for this, "Project following columns in the select clause as.
GroupByColumns.companyname
GroupByColumns.OrderdateYear
AggregationColumns.CountNumberOfIndividualOrders
AggregationColumns.CountNumberOfProductsOrders
AggregationColumns.TotalCost
AggregationColumns.TotalDiscountedCost
Finally to order by company name and orderdateYear( which are groups). Where im stuck is how to count the specific orders of qty that equal 1 as an aggregate function in the SELECT clause. I know it has to use the aggregate function COUNT because of the GROUP BY, just don't know how to. This is what I have.
SELECT
C.companyname, YEAR(O.orderdate) AS orderyear,OD.qty,
-- Where in the count function or if theres another way do I count all the
--single orders
--COUNT(OD.qty) AS indiviualorders,
(OD.unitprice * OD.qty - (OD.qty * OD.discount)) AS TotalCost,
(OD.qty * OD.discount) AS TotalDiscountedCost
FROM
Sales.Customers AS C
INNER JOIN
Sales.Orders AS O ON C.custid = O.custid
INNER JOIN
Sales.OrderDetails OD ON O.orderid = OD.orderid
INNER JOIN
Production.Products as P ON OD.productid = P.productid
GROUP BY
C.companyname, YEAR(O.orderdate)
ORDER BY
C.companyname, O.orderdate;
You case use a case statement inside a sum
SUM(CASE WHEN <xyz> THEN 1 ELSE 0 END)
But for the count of unique orders, use SELECT(DISTINCT ) on a key that is unique in the order table
SELECT COUNT(DISTINCT O.OrderID) As DistinctOrders FROM Table

Finding count in multiple tables

I need to find the amount of times a customer appears in the Orders and Requests tables respectively. However this script is producing the same count value for both places where COUNT is used. The value cannot possibly be the same so what am I doing wrong?
SELECT o.CustomerID,
COUNT(o.CustomerID) as OrdersPerCustomer,
COUNT(r.CustomerID) as RequestsPerCustomer
FROM Orders o
INNER JOIN [Customers] c on c.ID = o.CustomerID
INNER JOIN [Request] r on r.CustomerID = c.ID
GROUP BY o.CustomerID
You are multiplying the number of order and request records. I.e. by joining the tables, you get for, say, 3 orders and 4 requests for a customer 12 result rows. As the IDs will never be null in a record, COUNT(o.CustomerID) and COUNT(r.CustomerID) are just COUNT(*) (12 in my example, and not 3 and 4 as you expected).
The easiest approach:
select
customer_id,
(select count(*) from orders o where o.customerid = c.id) as o_count,
(select count(*) from request r where r.customerid = c.id) as r_count
from customers c;
The same with subqueries (derived tables) in the from clause:
select
customer_id,
coalesce(o.total, 0) as o_count,
coalesce(r.total, 0) as r_count
from customers c
left join (select customerid, count(*) as total from orders group by customerid) o
on o.customerid = c.id
left join (select customerid, count(*) as total from request group by customerid) r
on r.customerid = c.id;
When aggregating from multiple tables, always aggregate first and join then.

Left Join in Oracle SQL

I was going through an example of LEFT JOIN on w3schools.com.
http://www.w3schools.com/sql/sql_join_left.asp
SELECT Customers.CustomerName, Orders.OrderID
FROM Customers
LEFT JOIN Orders
ON Customers.CustomerID=Orders.CustomerID
ORDER BY Customers.CustomerName;
The above query will return me all customers with No Orders as NULL Order ID+ All customers having Orders with their Order Ids
How should I modify this query so that it returns All Customers with No Orders + All Customers having Orders with Order date as '1996-09-18'
Thanks in advance.
If you want customers with no orders and those with a specific order date, then you want a WHERE clause:
SELECT c.CustomerName, o.OrderID
FROM Customers c LEFT JOIN
Orders o
ON c.CustomerID = o.CustomerID
WHERE (o.CustomerID is NULL) OR (o.OrderDate = DATE '1996-09-18)
ORDER BY c.CustomerName;
If you wanted all customers with their order on that date (if they have one), then you would move the condition to the ON clause:
SELECT c.CustomerName, o.OrderID
FROM Customers c LEFT JOIN
Orders o
ON c.CustomerID = o.CustomerID AND o.OrderDate = DATE '1996-09-18
ORDER BY c.CustomerName;
Note the difference: the first filters the customers. The second only affects what order gets shown (and NULL will often be shown).

Compare a table with a count result from another table and add the names that have a zero count

I am counting how many times a company has ordered. Then I am only showing if a company has ordered less than 5 times. I am then checking it against the table with all company names to see what company has not ordered, which would not show up on the order table, then add their name on the displayed list.
What I have tried:
Select Orders.CustomerID, Count(Orders.CustomerID) AS OrderCount
From Orders Left Join Customers
On Orders.CustomerID = Customers.CustomerID
Group By Orders.CustomerID
Having Count(Orders.CustomerID) <5
This is totally wrong:
Select CustomerID
From Customers
Where EXISTS
(Select CustomerID, Count(CustomerID) AS 'OrderCount'
From Orders
Group BY CustomerID
Having Count(Orders.CustomerID) < 5)
I need to somehow compare the list of names before I ask it to see which ones have ordered less than 5 times.
Thanks
If you want to use LEFT JOIN, interchange the table names since you want to show values from Customers, otherwise use RIGHT JOIN instead.
SELECT Customers.CustomerID,
COUNT(Orders.CustomerID) AS OrderCount
FROM Customers
LEFT JOIN Orders
ON Orders.CustomerID = Customers.CustomerID
GROUP BY Customers.CustomerID
HAVING COUNT(Orders.CustomerID) < 5
using EXISTS()
SELECT CustomerID
FROM Customers c
WHERE EXISTS
(
SELECT 1
FROM Orders o
WHERE o.CustomerID = c.CustomerID
GROUP BY CustomerID
HAVING COUNT(CustomerID) < 5
)
Try this:
SELECT C.CustomerID, C.CustomerName, COUNT(O.CustomerID) AS OrderCount
FROM Customers C
LEFT JOIN Orders O ON O.CustomerID = C.CustomerID
GROUP BY C.CustomerID
HAVING OrderCount < 5
ORDER BY OrderCount, C.CustomerName