SQL: How do I count the rows of a summed column? - sql

How do I count the number of lines of a summed column?
I have built a query that will show how many items in each order, but I need to do a count of each Sum. My goal is to see how many people bought 3 items, how many bought 4, etc...
SELECT Orders.id,
sum(Quantity) AS "Box Count"
FROM OrderLines
INNER JOIN Orders ON OrderLines.Order_id = Orders.id
INNER JOIN Products ON OrderLines.Product_id = Products.id
WHERE (Products.ProductType_id = 2) AND Orders.shipdate > '10/01/2014'
GROUP BY Orders.id
It is returning this:
id Box Count
----- ---------
68015 6
69660 3
70923 3
72697 13
I want it to return this:
Box Count Total Count
--------- -----------
3 2
6 1
13 1

You could use a second query:
SELECT
[Box Count]
,COUNT(*) AS [Total Count]
FROM
(
SELECT
Orders.id,
sum(Quantity) AS "Box Count"
FROM OrderLines
INNER JOIN Orders ON OrderLines.Order_id = Orders.id
INNER JOIN Products ON OrderLines.Product_id = Products.id
WHERE (Products.ProductType_id = 2) AND Orders.shipdate > '10/01/2014'
GROUP BY Orders.id
) AS InnerQuery
GROUP BY [Box Count]
ORDER BY COUNT(*) DESC -- optional

Related

Selecting count with other columns through joined tables (Oracle SQL)

I have the following query that returns the number of orders a stock item has had:
select count(Orders.OrderID) from Stocks, Orders
where Stocks.StockID = Orders.StockID(+) group by Stocks.StockID;
This returns:
count(Orders.OrderID)
---------------------
0
1
2
0
1
...
However, I also want to display the name of the particular stock item alongside this query. So far, I have tried this, but the following error occurs...
select Stocks.Name, count(Orders.OrderID) from Stocks, Orders
where Stocks.StockID = Orders.StockID(+) group by Stocks.StockID;
The following error occurs:
select Stocks.Name, count(Orders.OrderID) from Stocks, Orders
*
Error: not a GROUP BY expression.
This should return:
Name count(Orders.OrderID)
---------- ---------------------
Item1 0
Item2 1
Item3 2
Item4 0
Item5 1
..... ...
Can anyone help? Thanks in advance.
It should be sth like:
select Stocks.StockID,Stocks.Name, count(Orders.OrderID)
from Stocks
left join Orders
on Stocks.StockID = Orders.StockID -- explicit outer join syntax
group by Stocks.StockID,Stocks.Name; -- matching with select column list
I suggest that you use the COUNT analytic function:
SELECT DISTINCT s.NAME,
COUNT(*) OVER (PARTITION BY s.STOCKID ORDER BY s.STOCKID) AS ON_ORDER
FROM STOCKS s
LEFT OUTER JOIN ORDERS o
ON s.STOCKID = o.STOCKID
dbfiddle here
Another option would be the following;
SELECT S.StockID, S.Name, Orders.[Count]
FROM Stocks AS S
CROSS APPLY
( SELECT COUNT(*) AS [Count]
FROM Orders AS O
WHERE S.StockID = O.StockID
) AS Orders
:)
From your description, it seems that you want to report one aggregate row for each stock name, or in other words group by the stock name:
select s.name as stock_name, count(o.orderid) as orders
from stocks s
left join orders o
on o.stockid = s.stockid
group by s.name;
That will give results like
STOCK_NAME ORDERS
----------- ------
X 123
Y 45
Z 6
You can include other columns from Stocks such as the Stock ID if you want:
select s.stockid, s.name as stock_name, count(o.orderid) as orders
from stocks s
left join orders o
on o.stockid = s.stockid
group by s.stockid, s.name;
which will give results like
STOCKID STOCK_NAME ORDERS
-------- ----------- ------
1 X 123
2 Y 45
3 Z 6

sql count on joined tables

I know that there a a bazillion of these on here, but I have spent an hour reading them and trying.
All I need to do is return a count of order and items assigned to order.
Totals, not each.
So if I have 300 order with exactly 3 items per order I need a return of:
-- OrderCount: 300 ItemCount: 900
SELECT COUNT (o.id) AS OrderCount, COUNT (i.id) AS ItemCount
FROM order_detail o JOIN item_detail i
ON i.job_id = o.id
WHERE o.customer_terminal_id = 182 AND o.requested_del_date = '2018-12-03'
This returns a count of everything and they are both the same.
I have tried with grouping as well.
Update:
This works:
SELECT COUNT (DISTINCT o.id) AS OrderCount, COUNT (i.id) AS ItemCount
FROM order_detail o JOIN item_detail i
ON i.job_id = o.id
WHERE o.customer_terminal_id = 182 AND o.requested_del_date = '2018-12-04'
but the real requirement is that some of the orders goto locations that have the same address. What I really need is the count of those unique addresses.
They are in a table called Common_Address_points which is joined to orders by
o.Delivery_Location_Id = Common_Address_Points.id
So desired output would be Orders, unique addresses and items
So in the above if all of them were 1 order to 1 address, except for 1 that had 10 orders going to the same address it would show:
Orders: 300
Addresses: 290
Items: 900
I think this does it but I have to verify:
SELECT COUNT (DISTINCT o.id) AS OrderCount, COUNT (i.id) AS ItemCount, COUNT (DISTINCT Common_Address_Point.Id) AS Addresses
FROM order_detail o
JOIN item_detail i ON i.job_id = o.id
JOIN Common_Address_Point ON Common_Address_Point.Id = o.Delivery_Location_Id
WHERE o.customer_terminal_id = 182 AND o.requested_del_date = '2018-12-04'
You can try below - you need to add distinct for order count - COUNT (distinct o.id)
SELECT COUNT (distinct o.id) AS OrderCount, COUNT (i.id) AS ItemCount
FROM order_detail o JOIN item_detail i
ON i.job_id = o.id
WHERE o.customer_terminal_id = 182 AND o.requested_del_date = '2018-12-03'

Display only those rows, whose SUM(price) is more than a certain number

I have 3 tables:
customers(name, id),
items(name, price, id),
purchases(id, items_id, customers_id)
When I run:
select
customers.name,
SUM(items.price) as "price"
from items
INNER JOIN purchases
ON items.id = purchases.item_id
INNER JOIN customers
ON purchases.customer_id = customers.id
GROUP BY customers.name
ORDER BY "price"
This is the result that I get:
"Anne Watson" "5.00"
"Craig Scott" "11.30"
"Michael Adam" "101.29"
"Jose Salvatierra" "899.00"
"Rolf Smith" "1174.50"
However, ​I would like to only show those rows, which have price over 100 (3 bottom ones in this case). How can I do that?
This is where you use a HAVING clause. It's similar to a WHERE clause, but applied after the GROUP BY.
HAVING SUM(items.price) > 100
If you want only SUM(prices) greater than 100 include a HAVING clause as below.
select
customers.name,
SUM(items.price) as "price"
from items
INNER JOIN purchases
ON items.id = purchases.item_id
INNER JOIN customers
ON purchases.customer_id = customers.id
GROUP BY customers.name
having sum(items.price) >100
Here is a solution with the WHERE function
select
customers.name,
SUM(items.price) as price
from items, purchases, customers
where
items.id = purchases.item_id and purchases.customer_id = customers.id and
items.price > 100
GROUP BY customers.name
ORDER BY price;

How to get the count and display only one row

I've got 3 Tables:
tblCustomer
CustomerID CustomerName
1 Customer 1
2 Customer 2
tblOrder
OrderID CustomerID OrderTypeID LoanNumber
1 1 1 98513542
2 1 1 71283527
3 1 1 10268541
4 1 1 61258965
tblOrderType
OrderTypeID OrderTypeName
1 Purchase
2 Rental
Now, I'm looking to return CustomerID, CustomerName, OrderTypeName and OrderCount, where OrderCount is how many of each order the customer has. I'm using the following query:
SELECT tblCustomer.CustomerID, tblCustomer.customerName, tblOrderType.OrderTypeName, tblOrder.OrderID
FROM tblCustomer
INNER JOIN tblOrder
ON tblCustomer.CustomerID = tblOrder.CustomerID
INNER JOIN tblOrderType
ON tblOrderType.OrderTypeID = tblOrder.OrderTypeID
It sort of works. It gets all I'm asking for, except for the OrderCount, obviously. The result is like this:
CustomerID CustomerName OrderTypeName OrderID
1 Customer 1 Purchase 1
1 Customer 1 Purchase 2
1 Customer 1 Purchase 3
1 Customer 1 Purchase 4
But what I'm looking for is this:
CustomerID CustomerName OrderTypeName OrderCount
1 Customer 1 Purchase 4
Now, I've tried adding Count() into the query at various places (Count(tblOrder.OrderID) for one), and I get an error that tblCustomer.CustomerID is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
This isn't a homework assignment. I just don't know a whole lot about sql and database interactions, since it wasn't taught in my school, and I've got a friend who's throwing scenarios at me.
You need to use grouping and aggregation:
SELECT tblCustomer.CustomerID, tblCustomer.customerName, tblOrderType.OrderTypeName,
count (tblOrder.OrderID) asOrderCOunt
FROM tblCustomer
INNER JOIN tblOrder
ON tblCustomer.CustomerID = tblOrder.CustomerID
INNER JOIN tblOrderType
ON tblOrderType.OrderTypeID = tblOrder.OrderTypeID
GROUP BY
tblCustomer.CustomerID,
tblCustomer.customerName,
tblOrderType.OrderTypeName
You can't use aggregate functions without grouping everything that isn't aggregated.
The error message does tell you the issue. Whenever you use an aggregation operator (COUNT, SUM, AVG, etc.), you have to tell the database how these should be aggregated together. You do this with a GROUP BY statement (the link there is for SQL Server, but it should generally apply to all database engines):
SELECT
C.CustomerID
,C.customerName
,OT.OrderTypeName
,COUNT(O.OrderID)
FROM tblCustomer C
INNER JOIN tblOrder O
ON C.CustomerID = O.CustomerID
INNER JOIN tblOrderType OT
ON OT.OrderTypeID = O.OrderTypeID
GROUP BY
C.CustomerID
,C.customerName
,OT.OrderTypeName
Try this, just add group by and count the grouped rows
SELECT tblCustomer.CustomerID, tblCustomer.customerName,
tblOrderType.OrderTypeName, tblOrder.OrderID,
COUNT(tblOrder.OrderID) as OrderCount
FROM tblCustomer INNER JOIN tblOrder
ON tblCustomer.CustomerID = tblOrder.CustomerID
INNER JOIN tblOrderType
ON tblOrderType.OrderTypeID = tblOrder.OrderTypeID
GROUP BY tblCustomer.CustomerID, tblCustomer.customerName,
tblOrderType.OrderTypeName

How to join tables together based on subselects?

I'm stuck to figure out how to write a query. Basically I've three tables (Orders, Products, Orders_Products) which I want to join together and apply some filtering.
Orders table:
ORDER_ID CUSTOMER_ID
1 1
2 2
Products table:
PRODUCT_ID PRODUCT_NAME PRODUCT_TITLE
1 'P1' 'T1'
2 'P1' 'T2'
3 'P2' 'T3'
4 'P2' 'T4'
5 'P2' 'T5'
6 'P3' 'T6'
Orders_Products table:
ORDER_ID PRODUCT_ID
1 1
1 3
2 1
2 3
2 6
For example I want to get all Orders which consists (exactly) of the products P1/T1 and P2/T3. I tried something like this, but that doesn't work:
SELECT * FROM Orders
LEFT JOIN Orders_Products ON Orders_Products.ORDER_ID = Orders.ORDER_ID
LEFT JOIN Products ON Orders_Products.PRODUCT_ID = Products.PRODUCT_ID
WHERE EXISTS (SELECT * FROM Product WHERE PRODUCT_NAME = 'P1' AND PRODUCT_TITLE = 'T1')
AND EXISTS (SELECT * FROM Product WHERE PRODUCT_NAME = 'P2' AND PRODUCT_TITLE = 'T3');
EDIT: To clarify what I really have to achieve. The user should be able to search for orders matching the given products. The user enters one or more product name / product title combinations and gets all the orders which have exactly this products associated. What I get (from a web application) are only the name/title combinations and I have to use those in a query to get the ORDER_ID.
SELECT OrderID, COUNT(*) AS ProductsCount
FROM Orders_Products
WHERE (PRODUCT_ID = 1 OR PRODUCT_ID = 3)
GROUP BY OrderID
HAVING COUNT(*) = 2
EDIT: Please ignore the above statement. See if the following works.
SELECT OrderID,
SUM(CASE PRODUCT_ID WHEN 1 THEN 1 WHEN 3 THEN 1 ELSE 3 END)
AS ProductsCount
FROM Orders_Products
GROUP BY OrderID
HAVING SUM(CASE PRODUCT_ID WHEN 1 THEN 1 WHEN 3 THEN 1 ELSE 3 END) = 2
I guess this should get you Orders which has only these 2 products.
You probably cannot write simple queries in MySQL to achieve this. But ANSI SQL supports table value constructor which simplifies this type of query.
This basic query returns the full list of orders (5 rows):
SELECT * FROM Products
JOIN Orders_Products ON Orders_Products.PRODUCT_ID = Products.PRODUCT_ID
JOIN Orders ON Orders_Products.ORDER_ID = Orders.ORDER_ID
This query with table value constructor returns the orders that you need:
SELECT * FROM Products
JOIN Orders_Products ON Orders_Products.PRODUCT_ID = Products.PRODUCT_ID
JOIN Orders ON Orders_Products.ORDER_ID = Orders.ORDER_ID
LEFT JOIN (VALUES('P1', 'T1'), ('P2', 'T3')) V(P_NAME, P_TITLE) ON PRODUCT_NAME = P_NAME AND PRODUCT_TITLE=P_TITLE
This query groups the above to returns the ORDER_ID where there is no order outside the required list (eliminating the orders that have rows containing null):
SELECT ORDER_ID FROM Products
JOIN Orders_Products ON Orders_Products.PRODUCT_ID = Products.PRODUCT_ID
JOIN Orders ON Orders_Products.ORDER_ID = Orders.ORDER_ID
LEFT JOIN (VALUES('P1', 'T1'), ('P2', 'T3')) V(P_NAME, P_TITLE) ON PRODUCT_NAME = P_NAME AND PRODUCT_TITLE=P_TITLE
GROUP BY ORDER_ID HAVING COUNT(*) = 2
Among open source databases, HSQLDB is one that supports table value constructor and other user friendly features of ANSI SQL:2008