Sql Aggregate function with join - sql

SELECT ORDERS.ORDERID,
ORDERS.CUSTOMERID,
ORDERS.EMPLOYEEID,
ORDERDETAILS.PRODUCTID,
ORDERDETAILS.UNITPRICE,
ORDERDETAILS.QUANTITY,
COUNT(ORDERS.ORDERID)
FROM ORDERS
LEFT JOIN ORDERDETAILS ON ORDERS.ORDERID=ORDERDETAILS.ORDERID
GROUP BY ORDERDETAILS.ORDERID
ERROR:Column 'ORDERS.OrderID' is invalid in the select list because it
is not contained in either an aggregate function or the GROUP BY
clause.

For using aggregate function selected column will also need to include in group by clause
SELECT
ORDERS.ORDERID,ORDERS.CUSTOMERID,ORDERS.EMPLOYEEID,ORDERDETAILS.PRODUCTID,ORD ERDETAILS.UNITPRICE,ORDERDETAILS.QUANTITY,
COUNT(ORDERS.ORDERID)
FROM ORDERS LEFT JOIN ORDERDETAILS ON
ORDERS.ORDERID=ORDERDETAILS.ORDERID
GROUP BY ORDERDETAILS.ORDERID,ORDERS.CUSTOMERID,ORDERS.EMPLOYEEID,ORDERDETAILS.PRODUCTID,ORD ERDETAILS.UNITPRICE,ORDERDETAILS.QUANTITY

Presumably, you intend this:
SELECT o.ORDERID, o.CUSTOMERID, o.EMPLOYEEID,
COUNT(od.ORDERID) as NUM_PRODUCTS
FROM ORDERS o LEFT JOIN
ORDERDETAILS od
ON o.ORDERID = od.ORDERID
GROUP BY o.ORDERID;
This produces one row per ORDERID with a count of the number of products (or more specifically orderdetails rows) in each order.
Notes:
All unaggregated columns in the SELECT should be GROUP BY keys.
You don't want to include unaggregated columns from ORDERDETAILS in the SELECT, because then an ORDER might have multiple rows in the result set.
You do want to use table aliases, so the query is easier to write and to read.

Related

Not in aggregate function or group by clause: org.hsqldb.Expression#59bcb2b6 in statement

I'm trying to group SUM(OrderDetails.Quantity) but keep getting the error Not in aggregate function or group by clause: org.hsqldb.Expression#59bcb2b6 in statement but since I already have an GROUP BY part I don't know what I'm missing
SQL Statement:
SELECT OrderDetails.CustomerID, Customers.CompanyName, Customers.ContactName, SUM(OrderDetails.Quantity)
FROM OrderDetails INNER JOIN Customers ON OrderDetails.CustomerID = Customers.CustomerID
WHERE OrderDetails.CustomerID = Customers.CustomerID
GROUP BY OrderDetails.CustomerID
ORDER BY OrderDetails.CustomerID ASC
I'm trying to create a table that shows customers and the amount of products they ordered, while also showing their CompanyName and ContactName.
Write this:
GROUP BY OrderDetails.CustomerID, Customers.CompanyName, Customers.ContactName
Unlike in MySQL, PostgreSQL, and standard SQL, in most other SQL dialects, it is not sufficient to group only by the primary key if you also want to project functionally dependent columns in the SELECT clause, or elsewhere. You have to explicitly GROUP BY all of the columns that you want to project.
Don't take the customer id from the orders table. Take it from the customers table. If you do so, this might work in your database:
SELECT c.CustomerID, c.CompanyName, c.ContactName, SUM(od.Quantity)
FROM OrderDetails od INNER JOIN
Customers c
ON od.CustomerID = c.CustomerID
GROUP BY c.CustomerID
ORDER BY c.CustomerID ASC;
Note that the WHERE clause does not need to repeat the conditions in the ON clause.
Your version won't work in standard SQL because od.CustomerId is not unique in OrderDetails. Many databases don't support this, so in these you need the additional columns:
SELECT c.CustomerID, c.CompanyName, c.ContactName, SUM(od.Quantity)
FROM OrderDetails od INNER JOIN
Customers c
ON od.CustomerID = c.CustomerID
GROUP BY c.CustomerID, c.CompanyName, c.ContactName
ORDER BY c.CustomerID ASC;
Even so, it is much, much better to take all columns from the same table. That would allow the SQL optimizer to use indexes on Customers.

Beginner: LEFT JOIN not doing what it should?

I'm having trouble with a really simple left join statement that's driving me nuts
I wanted to count the numbers of orders from each customer, that's fine, but I want to display the name, and I'm joining with the customers table and trying to select the name and it says that CustomerName is not part of an aggregate function, it's really weird.
SELECT Customers.CustomerName as 'Name',
COUNT(*) AS 'Order Count'
FROM Orders
LEFT JOIN Customers
ON Orders.CustomerID = Customers.CustomerID
GROUP BY Customers.CustomerID
Thanks for any tips.
You need to count the rows from the orders table, and the left join should be in the other direction:
SELECT c.customerid,
c.CustomerName as "Name",
COUNT(o.customerid) AS "Order Count"
FROM Customers c
LEFT JOIN Orders o ON o.CustomerID = cs.CustomerID
GROUP BY c.CustomerID, c.customername;
count() will ignore NULL values that come into the result due to the outer join so it will count the number of orders for each customers. Customers without orders will be show with a zero count.
Include CustomerName in Group BY instead of CustomerID
SELECT Customers.CustomerName as 'Name', COUNT(*) AS 'Order Count'
FROM Orders LEFT JOIN Customers ON Orders.CustomerID = Customers.CustomerID
GROUP BY Customers.CustomerName
If you are using SQL Server then try using OVER() without Group BY
SELECT Customers.CustomerName as 'Name', COUNT(*) OVER (PARTITION BY Customers.CustomerName ORDER BY Customers.CustomerName)AS 'Order Count'
FROM Orders LEFT JOIN Customers ON Orders.CustomerID = Customers.CustomerID
Modify as below. column used in group by clause should be in column queried in select clause
SELECT Customers.CustomerName as 'Name',
COUNT(*) AS 'Order Count'
FROM Orders
LEFT JOIN Customers
ON Orders.CustomerID = Customers.CustomerID
GROUP BY Customers.CustomerName
I have just reordered your query,please try this it will definitely work for you.
SELECT Customers.CustomerName as 'Name',
COUNT(*) AS 'Order Count'
FROM Customers
LEFT JOIN Orders
ON Customers.CustomerID=Orders.CustomerID
GROUP BY Customers.CustomerID
A simple approach to get all the columns in the customers table is to use a correlated subquery:
select c.*, -- or whatever columns you want
(select count(*)
from orders o
where o.CustomerID = c.CustomerID
) as order_count
from customers c;
Because this avoids the outer GROUP BY, this also has the advantage of having better performance in most databases, particularly with an index on orders(CustomerId). Plus, it returns 0 if the customer has no orders. And, it allows you to choose any or all of the columns from Customers.
The correct way to get the counts you want is to count a column from Orders:
SELECT c.CustomerName, c.CustomerID,
COUNT(o.CustomerId) AS Order_Count
FROM Customers c LEFT JOIN
Orders o
ON o.CustomerID = c.CustomerID
GROUP BY c.CustomerID, c.CustomerName;
Notes:
The Customers table goes first in the LEFT JOIN because presumably you want all rows in Customers.
Table aliases make the query easier to write and to read.
Do not use single quotes for column aliases, even if your database supports it. The best method is to choose aliases that do not need to be supported.
Include the CustomerId in the logic, just in case two customers have the same name.
Count a column from Orders so you get a count of 0 for customers with no orders.

Left Join and Sum Each Individual Row

I have a table named orders (orderID, customerID, purchaseDate, and paymentType) and orderLine (orderID, upc, productName, quantity, price). I'm trying to join these two tables so that it primarily shows the orders table but then has a total price column for each order on the far right. This is what I have so far but it's adding the total for every single row into one. I want each individual row to have their own sum by multiplying quantity*price based on the orderID.
SELECT orders.orderID, SUM(orderLine.price * orderLine.quantity)
FROM orderLine
LEFT JOIN orders
ON orders.orderID = orderLine.orderID
You need to add group by clause
SELECT orders.orderID, SUM(orderLine.price * orderLine.quantity)
FROM orderLine
LEFT JOIN orders
ON orders.orderID = orderLine.orderID
grou by orders.orderID
SELECT orders.orderID, (SUM(orderLine.price * orderLine.quantity) Over (Order
By orders.orderID))
FROM orderLine
LEFT JOIN orders
ON orders.orderID = orderLine.orderID
Group By orders.orderID
You want all orders, so start with orders. Table aliases also make the query easier to write and to read:
SELECT o.orderID, SUM(ol.price * ol.quantity)
FROM orders o LEFT JOIN
orderLine ol
ON o.orderID = ol.orderID
GROUP BY o.orderID;
Then the answer to your question is GROUP BY.
You would have a very poor data model if you have orderlines that don't have a corresponding order, which is what your version of the query suggests.

Northwind query with join on Orders and Order Details

I have written a query like below using NorthWind.
select COUNT(o.OrderId) as Orders
from Orders o
join [Order Details] od on o.OrderID = od.OrderID
The table Orders has 830 data. However when I join Orders on Order Details query gives me the number of data inside Order Details table which is 2155.
Why is the query result not 830?
select COUNT( distinct o.OrderId) as Orders
from Orders o
join [Order Details] od on o.OrderID = od.OrderID
It's because of the join. The details create a one to many relationship repeating order ID in your results. The repeated ID is then counted each time; thus inflating the count to match record count in order details. This can be avoided by either not doing the join, or by using a distinct count of orderID as listed above.
If you need any of the details from order details, or you want to exclude from your count orders without details, then you need the join. Otherwise I'd remove it and the distinct as it's just generating overhead and adding cost to getting your results.
Additionally though, if you have orders without details then and you want them included, you need to alter your join to be a LEFT join not just an join (Inner). As the join will exclude orders w/o details. If you don't want those orders w/o details in your count then an inner join is appropriate.

finding average dollar amount of an order

I am trying to find the average dollar amount of an order. I have calculated the average order Total but I need an average that takes into account the fact that not all Orders have a corresponding OrderItems.
This is a homework question and it is as follows:
What is the average $$ value of an order? To get the answer, you need
to add up all the order values and divide this by the
number of orders. There are two possible averages on this question,
because not all of the order numbers in the ORDERS table are in the
ORDERITEMS table... You will calculate and display both averages.
I have writtern the one ignoring orders with no OrderItem, but not sure of how to go about the second case.
SELECT SUM(OrderItems.qty*INVENTORY.price) / COUNT(*) AS dollarValue
FROM Orders, OrderItems, Inventory
WHERE ORDERS.orderid = OrderItems.orderid AND OrderItems.partid = Inventory.partid
Link To DB Diagram
The Avg function will not replace NULL with zero; it will exclude NULL from its calculation. If you have Order rows which have no OrderItem, you need to use Left Joins. A trick you can use in SQL Server is to nest the joins like so (note the parentheses):
Select Avg(OI.Qty * I.Price)
From Orders As O
Left Join (OrderItems As OI
Join Inventory As I
On I.PartId = OI.PartId)
On OI.OrderId = O.OrderId
This will join the Inventory table to the OrderItems table before it Left Joins that result to the Orders table. In this way, OI.Qty and I.Price with both return NULL for Orders that have no OrderItems and be excluded from the calculation. An equivalent approach to the above would be to use two Left Joins:
Select Avg(OI.Qty * I.Price)
From Orders As O
Left Join OrderItems As OI
On OI.OrderId = O.OrderId
Left Join Inventory As I
On I.PartId = OI.PartId
If you wanted to count Orders with no OrderItems as zero, then you need to covert those nulls to zero using Coalesce:
Select Avg(OI.Qty * I.Price) As Avg_ExcludingNull
, Avg( Coalesce(OI.Qty * I.Price,0) ) As Avg_NullAsZero
From Orders As O
Left Join (OrderItems As OI
Join Inventory As I
On I.PartId = OI.PartId)
On OI.OrderId = O.OrderId
SQL has an aggregate function for calculating the average: AVG()
SELECT AVG(OrderItems.qty*INVENTORY.price) AS dollarValue
FROM Orders, OrderItems, Inventory
WHERE ORDERS.orderid = OrderItems.orderid AND OrderItems.partid = Inventory.partid
While we're here, may I suggest you use the more modern JOIN syntax:
SELECT AVG(OrderItems.qty*INVENTORY.price) AS dollarValue
FROM Orders
JOIN OrderItems ON ORDERS.orderid = OrderItems.orderid
JOIN Inventory ON OrderItems.partid = Inventory.partid