Left Join and Sum Each Individual Row - sql

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.

Related

Sql Aggregate function with join

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.

SQL query based on aggregate and join

I am new to SQL and I am trying to join two tables by performing aggregate and join functions. I am using Orderdetails and PurchaseOrderDetails tables to join based on OrderID.
I want to compare the sum on quantity in the OrderDetails table and sum on quantity in the PurchaseOrderDetails table and sum(PurchaseOrderDetails.quantity) != sum(OrderDetails.quantity) based on particular orderID and also join them based on orderID.
I have following two tables:
OrderDetails table design
SELECT TOP 1000 [lineID]
,[orderID]
,[itemNumber]
,[itemDesc]
,[qty]
,[price]
,[priceType]
,[contract]
,[supplierPartID]
,[uom]
,[classDomain]
,[classValue]
,[comments]
,[shipping]
,[tax]
,[reqDeliveryDate]
,[fillInfo]
,[invoiceID]
,[Version]
,[productid]
,[eta]
,[lineOrderID]
FROM [bfdb].[dbo].[OrderDetails]
and PurchaseOrderDetails table design is:
SELECT TOP 1000 [podID]
,[poID]
,[distPN]
,[mfrID]
,[itemNumber]
,[itemName]
,[qty]
,[price]
,[invoiceID]
,[orderID]
,[itemReceived]
,[qtyReceived]
,[distConf]
,[distFillStatus]
,[distLastUpdate]
,[distTracking]
,[distFillDesc]
,[Version]
,[productid]
,[lineOrderID]
FROM [bfdb].[dbo].[PurchaseOrderDetails]
I tried the following query but getting the incorrect result. Suppose if it has 1 row in the OrderDetails table and 2 rows in the PurchaseOrderDetails, then the sum for OrderDetails becomes double after joining. Same with 1 row in PurchaseOrderDetails table if I have more than 1 row in OrderDetails table for that
SELECT
od.orderID, SUM(pod.qty) as Totalqty, SUM(od.qty) as od_qty
FROM
PurchaseOrderDetails pod
INNER JOIN
OrderDetails od ON od.orderID = pod.orderID AND pod.orderID = 15506
GROUP BY
od.orderID
Here is the snapshot of my data
You need to first sum the quantities by orderid for each of these two tables separately and then join. Try this:
select od.orderID, pod.qty as Totalqty, od.qty as od_qty
from (
select orderid, sum(pod.qty) as qty
from PurchaseOrderDetails
group by orderid
) pod
inner join (
select orderid, sum(pod.qty) as qty
from OrderDetails
group by orderid
) od on od.orderid = pod.orderid
You need to simply change the AND condition to a WHERE clause, try this code below:
SELECT od.orderID, SUM(pod.qty) as Totalqty, SUM(od.qty) as od_qty FROM PurchaseOrderDetails pod INNER JOIN OrderDetails od ON od.orderID = pod.orderID WHERE pod.orderID = 15506 GROUP BY od.orderID

Select all orders by one customer

I have three tables, orders, orders_details and customers. I need to select orders by one customer for the orders table so I did this
orders columns:
id
customer_id
created
vat
discount
amount
paid
orders_details columns:
id
order_id
cost
qty
product
The SQL I used
SELECT
orders.*,
SUM(orders_details.qty*orders_details.cost) as amount,
SUM(orders_details.qty) AS qty
FROM
orders,
orders_details,
customers
WHERE
orders.customer_id = customers.id
AND orders_details.order_id = orders.id
AND orders.customer_id = 1
but I am getting a wrong qty of 30 instead of 20 and the amount is wrong
If you want to aggregate per order you need a GROUP BY clause. Also you should use proper JOIN syntax, and might consider using aliases to make the query more compact.
SELECT
o.*,
SUM(od.qty * od.cost) AS amount,
SUM(od.qty) AS qty
FROM orders o
INNER JOIN orders_details od ON od.order_id = o.id
INNER JOIN customers c ON o.customer_id = c.id -- not used, might be excluded
WHERE o.customer_id =1
GROUP BY o.id
Depending on what database system you are using you might need to include all columns referenced in o.* in the GROUP BY:
GROUP BY o.id, o.customer_id, o.created, o.vat, o.discount, o.amount, o.paid
Last note: as you don't seem to use any data from the customers table you probably could exclude that table altogether.
You're missing a GROUP BY clause which I'm guessing should be on orders.id.

Using DISTINCT in SQL Query

How do i use DISTINCT command in a SQL query to show the supplier id, company name, and the number of distinct products from that supplier that were ordered prior to a specific date? I ran the code in Access but it doesn't translate over to SQL efficiently. The table appears.
[Supplier ID Company Name Product Name Order Date
1 Exotic Liquids Chang 17-Aug-94
1 Exotic Liquids Chang 22-Nov-94
1 Exotic Liquids Aniseed Syrup 26-Sep-94]
The code I have so far is the following. Where I get confused is where to put the DISTINCT statement. Should it be immediately after the Select? Should it go in Parentheses in addition to the SELECT? Excuse my lack of knowledge on this subject in advance.
SELECT Suppliers.SupplierID, Customers.CompanyName, Products.ProductName,
Orders.OrderDate
FROM Suppliers INNER JOIN
Products ON Suppliers.SupplierID = Products.SupplierID CROSS JOIN
Customers INNER JOIN
Orders ON Customers.CustomerID = Orders.CustomerID
WHERE Orders.OrderDate <='1/1/1999'
ORDER BY Suppliers.SupplierID
You can either distinct by all columns selected :
SELECT DISTINCT
Suppliers.SupplierID, Customers.CompanyName, Products.ProductName,
Orders.OrderDate
FROM
Suppliers INNER JOIN
Products ON Suppliers.SupplierID = Products.SupplierID CROSS JOIN
Customers INNER JOIN
Orders ON Customers.CustomerID = Orders.CustomerID
WHERE
Orders.OrderDate <='1/1/1999'
ORDER BY
Suppliers.SupplierID
or use group by instead if you need to distinct only by SupplierID. DISTINCT is not a function, hence DISTINCT(Suppliers.SupplierID) means the same as simply put DISTINCT word after SELECT in this case (see the 2nd reference below).
For Reference :
http://blog.sqlauthority.com/2007/12/20/sql-server-distinct-keyword-usage-and-common-discussion/
http://weblogs.sqlteam.com/jeffs/archive/2007/10/12/sql-distinct-group-by.aspx
I'm pretty sure it's this:
SELECT DISTINCT(Suppliers.SupplierID), Customers.CompanyName, Products.ProductName,Orders.OrderDate
FROM Suppliers INNER JOIN
Products ON Suppliers.SupplierID = Products.SupplierID CROSS JOIN
Customers INNER JOIN
Orders ON Customers.CustomerID = Orders.CustomerID
WHERE Orders.OrderDate <='1/1/1999'
ORDER BY Suppliers.SupplierID

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