Return the rows that are above average - sql

I'm trying to find the countries from which the number of orders is higher than the average. This is as far as I got. The problem is (see picture) that the count of orders for each country is off, as it should be different for everyone
SELECT avg(NumberOrders) as avg,
Customers.Country,
NumberOrders
FROM Customers,
(SELECT COUNT(Orders.OrderId) AS NumberOrders
FROM Customers JOIN
Orders ON Customers.CustomerID = Orders.CustomerID
GROUP BY Customers.Country) nested
GROUP BY Customers.Country
HAVING NumberOrders > avg;
Output

If your DBMS supports Windowed Aggregates (almost all besides MySQL & Access):
select *
from
(
SELECT Customers.Country,
COUNT(Orders.OrderId) AS NumberOrders, -- count per country
AVG(COUNT(Orders.OrderId)) OVER () AS avgOrders -- average count
FROM Customers
JOIN Orders
ON Customers.CustomerID = Orders.CustomerID
GROUP BY Customers.Country
) nested
WHERE NumberOrders > avgOrders
Edit:
For DBMSes not supporting Windowed Aggregates it's way more complicated:
SELECT Customers.Country,
COUNT(Orders.OrderId) AS NumberOrders -- count per country
FROM Customers
JOIN Orders
ON Customers.CustomerID = Orders.CustomerID
GROUP BY Customers.Country
HAVING COUNT(Orders.OrderId) >
( select avg(NumberOrders)
from
(
SELECT Customers.Country,
COUNT(Orders.OrderId) AS NumberOrders -- count per country
FROM Customers
JOIN Orders
ON Customers.CustomerID = Orders.CustomerID
GROUP BY Customers.Country
) AS dt
)
If the DBMS supports Common Table Expressions this can be simplified:
with cte as
(
SELECT Customers.Country,
COUNT(Orders.OrderId) AS NumberOrders -- count per country
FROM Customers
JOIN Orders
ON Customers.CustomerID = Orders.CustomerID
GROUP BY Customers.Country
)
select *
from cte
WHERE NumberOrders >
(
select avg(NumberOrders) from cte
)

Related

Order customers based on the purchases sum

I have this SQL
SELECT customers.first_name
FROM customers
INNER JOIN orders ON customer.id = orders.customer_id
GROUP BY first_name
HAVING SUM(orders.price) > 100;
But I want all customers to be listed in a table from the highest purchase price of their order to the lowest.
You can use next simple ORDER BY:
SELECT customers.first_name, SUM(orders.price) orders_price
FROM customers
INNER JOIN orders ON customers.id = orders.customer_id
GROUP BY first_name
ORDER BY orders_price DESC;
MySQL order by fiddfe
Also you can use LEFT JOIN and COALESCE function for select customers without orders:
SELECT
customers.first_name,
COALESCE(SUM(orders.price), 0) orders_price FROM customers
LEFT JOIN orders ON customers.id = orders.customer_id
GROUP BY first_name
ORDER BY orders_price DESC;
MySQL LEFT JOIN & COALESCE

How to find result set from two different date comparisons?

How to find result set from two different date comparisons
How to find period1_label_cost, period2_label_cost in this query without using this query ?
Only period1_label_cost column value is getting
select distinct customers.id as customer_id, customers.first_name as first_name, customers.last_name as last_name, SUM(orders.total_cost) as period1_label_cost
from customers inner join orders
on customers.id= orders.customer_id
where
date(orders.created_at) between 'start_date1' and 'end_date1'
group by customers.id , customers.first_name, customers.last_name, customers.preferred
having(SUM(orders.total_cost) > sales_exceeded
intersect
select distinct customers.id as customer_id, customers.first_name as first_name, customers.last_name as last_name,
customers.preferred as preferred, SUM(orders.total_cost) as period2_label_cost
from customers inner join orders
on customers.id= orders.customer_id
where
date(orders.created_at) between start_date2 and end_date2
group by customers.id , customers.first_name, customers.last_name, customers.preferred
having( SUM(orders.total_cost) < sales_below) order by first_name asc
Are you looking for mere joins? Put your two queries in your FROM clause and join them on customer_id, then join with the customers table and show the results.
select
c.id as customer_id,
c.first_name,
c.last_name,
c.preferred,
period1.label_cost as period1_label_cost,
period2.label_cost as period2_label_cost
from
(
select
customer_id,
sum(total_cost) as label_cost
from orders
where date(created_at) between <start_date1> and <end_date1>
group by customer_id
having sum(total_cost) > <sales_exceeded>
) period1
join
(
select
customer_id,
sum(total_cost) as label_cost
from orders
where date(created_at) between <start_date2> and <end_date2>
group by customer_id
having sum(total_cost) < <sales_below>
) period2 on period2.customer_id = period1.customer_id
join customers c on c.id = period1.customer_id;

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

Conditions in SQL subqueries

I made this query and now I have to find different ways which will return same results.
SELECT CompanyName, COUNT(Orders.OrderID) Orders
FROM Customers LEFT OUTER JOIN Orders ON Customers.CustomerID = Orders.CustomerID
WHERE Country LIKE 'Germany' OR Country LIKE 'Brazil'
GROUP BY CompanyName HAVING COUNT(Orders.OrderID) >= 10
ORDER BY Orders ASC;
So I made this but I have no idea where should I put the ">=10" condition. Could someone tell me how to do it? I tried some ways but it didn't work.
SELECT CompanyName, (SELECT COUNT(OrderID) FROM Orders WHERE Customers.CustomerID = Orders.CustomerID) AS Orders
FROM Customers
WHERE Country LIKE 'Germany' OR Country LIKE 'Brazil'
ORDER BY Orders ASC;
It's for MS SQL Server...
SELECT CompanyName, COUNT(Orders.OrderID) Orders
FROM Customers JOIN Orders ON Customers.CustomerID = Orders.CustomerID
WHERE Country IN('Germany','Brazil')
GROUP BY CompanyName
HAVING COUNT(Orders.OrderID) >= 10
ORDER BY Orders ASC;
SELECT
COMPANYNAME,
( SELECT
COUNT ( ORDERID )
FROM
ORDERS
WHERE
CUSTOMERS.CUSTOMERID = ORDERS.CUSTOMERID
HAVING
COUNT ( ORDERID ) >= 10 )
AS ORDERS
FROM
CUSTOMERS
WHERE
COUNTRY LIKE 'Germany'
OR COUNTRY LIKE 'Brazil'
ORDER BY
ORDERS ASC;

SQL Query for counting number of orders per customer and Total Dollar amount

I have two tables
Order with columns:
OrderID,OrderDate,CID,EmployeeID
And OrderItem with columns:
OrderID,ItemID,Quantity,SalePrice
I need to return the CustomerID(CID), number of orders per customer, and each customers total amount for all orders.
So far I have two separate queries. One gives me the count of customer orders....
SELECT CID, Count(Order.OrderID) AS TotalOrders
FROM [Order]
Where CID = CID
GROUP BY CID
Order BY Count(Order.OrderID) DESC;
And the other gives me the total sales. I'm having trouble combining them...
SELECT CID, Sum(OrderItem.Quantity*OrderItem.SalePrice) AS TotalDollarAmount
FROM OrderItem, [Order]
WHERE OrderItem.OrderID = [Order].OrderID
GROUP BY CID
I'm doing this in Access 2010.
You would use COUNT(DISTINCT ...) in other SQL engines:
SELECT CID,
Count(DISTINCT O.OrderID) AS TotalOrders,
Sum(OI.Quantity*OI.SalePrice) AS TotalDollarAmount
FROM [Order] O
INNER JOIN [OrderItem] OI
ON O.OrderID = OI.OrderID
GROUP BY CID
Order BY Count(DISTINCT O.OrderID) DESC
Which Access unfortunately does not support. Instead you can first get the Order dollar amounts and then join them before figuring the order counts:
SELECT CID,
COUNT(Orders.OrderID) AS TotalOrders,
SUM(OrderAmounts.DollarAmount) AS TotalDollarAmount
FROM [Orders]
INNER JOIN (SELECT OrderID, Sum(Quantity*SalePrice) AS DollarAmount
FROM OrderItems GROUP BY OrderID) AS OrderAmounts
ON Orders.OrderID = OrderAmounts.OrderID
GROUP BY CID
ORDER BY Count(Orders.OrderID) DESC
If you need to include Customers that have orders with no items (unusual but possible), change INNER JOIN to LEFT OUTER JOIN.
Create a query which uses your 2 existing queries as subqueriers, and join the 2 subqueries on CID. Define your ORDER BY in the parent query instead of in a subquery.
SELECT
sub1.CID,
sub1.TotalOrders,
sub2.TotalDollarAmount
FROM
(
SELECT
CID,
Count(Order.OrderID) AS TotalOrders
FROM [Order]
GROUP BY CID
) AS sub1
INNER JOIN
(
SELECT
CID,
Sum(OrderItem.Quantity*OrderItem.SalePrice)
AS TotalDollarAmount
FROM OrderItem INNER JOIN [Order]
ON OrderItem.OrderID = [Order].OrderID
GROUP BY CID
) AS sub2
ON sub1.CID = sub2.CID
ORDER BY sub1.TotalOrders DESC;