Inner join multiple SQL table and do calculations - sql

I need to calculate in September 1996 what proportion of each product contribute to total revenue. The data are in 3 tables
Table #1: OrderDetails
OrderDetailID OrderID ProductID Quantity-
-------------------------------------------
1 10248 11 12
2 10248 42 10
3 10248 72 5
4 10249 14 9
5 10249 51 40
6 10250 41 10
Table #2: Products
ProductID ProductName SupplierID CategoryID Unit Price
-----------------------------------------------------------------------------------------------------
1 Chais 1 1 10 boxes x 20 bags 18
2 Chang 1 1 24 - 12 oz bottles 19
3 Aniseed Syrup 1 2 12 - 550 ml bottles 10
4 Chef Anton's Cajun Seasoning 2 2 48 - 6 oz jars 22
5 Chef Anton's Gumbo Mix 2 2 36 boxes 21.35
6 Grandma's Boysenberry Spread 3 2 12 - 8 oz jars 25
7 Uncle Bob's Organic Dried Pears 3 7 12 - 1 lb pkgs. 30
Table #3: Orders
OrderID CustomerID EmployeeID OrderDate ShipperID
------------------------------------------------------
10248 90 5 1996-07-04 3
10249 81 6 1996-07-05 1
10250 34 4 1996-07-08 2
10251 84 3 1996-07-08 1
10252 76 4 1996-07-09 2
10253 34 3 1996-07-10 2
10254 14 5 1996-07-11 2
I guess the steps are:
inner join OrderDetails with Order by OrderID to only show Orders in Sep 1996
inner join the result in step 1 with Products by ProductID and calculate each product's revenue percentage over total revenue
I've done step one
SELECT
Orders.OrderID,
Orders.OrderDate,
OrderDetails.Quantity,
OrderDetails.ProductID
FROM
Orders
INNER JOIN
OrderDetails ON Orders.OrderID = OrderDetails.OrderID
WHERE
OrderDate LIKE '1996-09%';
The expected result:
OrderID OrderDate Quantity ProductID
-----------------------------------------
10295 1996-09-02 4 56
10296 1996-09-03 12 11
10296 1996-09-03 30 16
10296 1996-09-03 15 69
10297 1996-09-04 60 39
But I don't know how to do step 2. Any suggestions please ? thank you very much!

Use window functions:
SELECT od.ProductID,
SUM(od.Quantity),
SUM(od.Quantity) * 1.0 / SUM(SUM(od.Quantity)) OVER () as ratio
FROM Orders o INNER JOIN
OrderDetails od
ON o.OrderID = od.OrderID
WHERE o.OrderDate >= '1996-09-01' AND o.OrderDate < '1996-10-01'
GROUP BY od.ProductID;

It looks like W3Cschool's interactive SQL practice doesn’t support window functions. Moreover, I didn’t find a way to define a variable, so had to mention the same constant (year and month) twice. In real life I would suggest using of analytic functions or at least declare a variable for a value used in several places.
Anyway, it looks like the following code doing what you need:
SELECT
od.ProductID,
MIN(o.OrderDate) as SalesStart,
MAX(o.OrderDate) as SalesEnd,
SUM(od.Quantity) as SoldQty,
SUM(od.Quantity * p.Price) as SoldAmt,
SUM(od.Quantity * p.Price) * 1.0 /
(
SELECT SUM(odAll.Quantity * pAll.Price)
FROM OrderDetails odAll
INNER JOIN Orders as oAll
ON oAll.OrderID = odAll.OrderID
INNER JOIN Products as pAll
ON odAll.ProductID = pAll.ProductID
WHERE oAll.OrderDate LIKE '1996-09%') as PortionInTotalSales
FROM Orders as o
INNER JOIN OrderDetails as od
ON o.OrderID = od.OrderID
INNER JOIN Products as p
ON od.ProductID = p.ProductID
WHERE o.OrderDate LIKE '1996-09%'
GROUP BY od.ProductID

Related

SELECT all orders data including matched and unmatched orders SQL

I need to select all orders data including orders which lead to a transaction + those which didn't lead to a transaction.
Knowing that:
SELECT * FROM Buy_Orders
OrderID OrderQuantity OrderPrice OrderPlacementDate
-----------------------------------------------------------------
12 11 103 2021-10-12 14:02:22.703
14 6 100 2021-10-12 14:04:24.700
14 0 100 2021-10-12 14:07:27.206
17 3 80 2021-10-12 14:08:22.703
12 0 103 2021-10-12 14:09:21.501
20 20 23 2021-10-12 14:11:23.705
SELECT * FROM Sell_Orders
OrderID OrderQuantity OrderPrice OrderPlacementDate
--------------------------------------------------------------
9 2 13 2021-10-12 14:05:25.705
23 7 100 2021-10-12 14:07:27.205
23 1 100 2021-10-12 14:07:27.206
33 9 90 2021-10-12 14:08:28.403
90 1 103 2021-10-12 14:09:21.500
90 0 103 2021-10-12 14:09:21.501
SELECT * FROM Transactions
TransactionID TransactionQuantity TransactionPrice SellOrderID BuyOrderID
---------------------------------------------------------------------------------------
113 6 100 23 14
123 1 103 90 12
Logic for TransactionID 113 ( SellOrderID 23 + BuyOrderID 14 ): transaction created when order 23 entered the order book at 2021-10-12 14:07:27.205 and matched order 14 (partial fill). That's why there was an update on both impacted orders (23 & 14) at 2021-10-12 14:07:27.206 in tables Sell_Orders and Buy_Orders. So, the match with quantity=6 will create an update on order 23 to re-enter the orderbook with quantity=1 and an update on order 14 to re-enter the order book with quantity=0 at 2021-10-12 14:07:27.206.
I have tried the following SQL query but with no chance. I assume I'm not fluent with SQL. Please help!
SELECT
o.OrderID
o.OrderQuantity
o.OrderPlacementDate
t.TransactionID
FROM (
SELECT *
from
Sell_Orders
UNION
SELECT *
from
Buy_Orders ) o
LEFT JOIN (
SELECT
TransactioID
FROM
Transactions ) t on t.SellOrderID = o.OrderID or t.BuyOrderID = o.OrderID
I expect to have this table as an output:
OrderID TransactionID OrderQuantity OrderPrice OrderPlacementDate
---------------------------------------------------------------------------
12 NULL 1 103 2021-10-12 14:02:22.703
14 NULL 6 100 2021-10-12 14:04:24.700
9 NULL 2 13 2021-10-12 14:05:25.705
23 NULL 7 100 2021-10-12 14:07:27.205 -----> 1st Transaction
23 113 1 100 2021-10-12 14:07:27.206
14 113 0 100 2021-10-12 14:07:27.206
17 NULL 3 80 2021-10-12 14:08:22.703
33 NULL 9 90 2021-10-12 14:08:28.403
90 NULL 1 103 2021-10-12 14:09:21.500 -----> 2nd Transaction
90 123 0 103 2021-10-12 14:09:21.501
12 123 0 103 2021-10-12 14:09:21.501
20 NULL 20 23 2021-10-12 14:11:23.705
Your last subquery doesn't include the order id's, so there's nothing to join t on. Just don't use a subquery.
LEFT JOIN (
SELECT
TransactioID
FROM
Transactions ) t on t.SellOrderID = o.OrderID or t.BuyOrderID = o.OrderID
Becomes...
LEFT JOIN
Transactions t
ON t.SellOrderID = o.OrderID
OR t.BuyOrderID = o.OrderID
EDIT:
You also want each transaction to join on just one buy order, and one sell order, which requires adding a ranking id to each order.
Provided that (OrderID, OrderPlacementDate) is guaranteed to be unique, that can be accomplished with...
SELECT
o.*,
t.TransactionID
FROM
(
SELECT
*,
'Sell' AS OrderType,
ROW_NUMBER() OVER (PARTITION BY OrderID ORDER BY OrderPlacementDate DESC) AS OrderIDRank
FROM
Sell_Orders
UNION ALL -- ALWAYS use ALL unless you KNOW a reason otherwise
SELECT
*,
'Buy' AS OrderType,
ROW_NUMBER() OVER (PARTITION BY OrderID ORDER BY OrderPlacementDate DESC) AS OrderIDRank
FROM
Buy_Orders
)
AS o
LEFT JOIN
Transactions AS t
ON o.OrderIDRank = 1
AND o.OrderID IN (t.BuyOrderID, t.SellOrderID)
This query gets expected table
with All_Orders as(
SELECT *,'S' as side from Sell_Orders
UNION ALL
SELECT *,'B' as side from Buy_Orders
)
SELECT
o.OrderID
,case when TransactionQuantity<=OrderQuantity then null
else TransactionId
end TransactionId
,o.OrderQuantity
,o.OrderPlacementDate
-- for info
,t.TransactionID as realTranId
,o.Side,TransactionQuantity
FROM All_Orders o LEFT JOIN Transactions t on t.SellOrderID = o.OrderID or t.BuyOrderID = o.OrderID
order by OrderPlacementDate
Source row
12, 11, 103, 2021-10-12 14:02:22.703
may be ?
12, 1, 103, 2021-10-12 14:02:22.703
DBFiddle example

multiple two column values with grouping by a column

I've two tables tblOrder and tblOrderDetails. I want to get order no, total price per order (Quantity*UnitCost) and OrderDate as given below.
Order No
Total
OrderDate
ORD 1
3000
01/01/2021
ORD 2
2750
01/03/2021
What I've tried is giving me quantity is not a part of aggregate function.
SELECT tblOrder.OrderNo, tblOrderDetails.UnitCost*tblOrderDetails.Quantity AS Total, OrderDate
FROM tblOrderDetails INNER JOIN tblOrder ON tblOrderDetails.OrderId = tblOrder .OrderId
GROUP BY tblOrder.OrderNo;
Table structures and data
Table tblOrder:
OrderId
OrderNo
OrderDate
1
ORD 1
01/01/2021
2
ORD 2
01/03/2021
Table tblOrderDetails:
OrderDetailId
Quantity
UnitCost
OrderId
1
100
30
1
2
50
40
2
2
10
15
2
2
20
30
2
select o.OrderNo
,od.total
,o.OrderDate
from
(
select OrderId
,sum(Quantity*UnitCost) as total
from tblOrderDetails
group by OrderId
) od join tblOrder o on o.OrderId = od.OrderId
OrderNo
total
OrderDate
ORD 1
3000
2021-01-01
ORD 2
2750
2021-01-03
Fiddle
Your requirements are not 100% clear, but maybe, you can just do this, without any subquery:
SELECT tblOrder.OrderNo,
SUM(tblOrderDetails.UnitCost*tblOrderDetails.Quantity) AS Total,
OrderDate
FROM tblOrderDetails
INNER JOIN tblOrder ON tblOrderDetails.OrderId = tblOrder.OrderId
GROUP BY tblOrder.OrderNo,OrderDate;
To see the difference to Danny's answer - which might also be fine - have a look here: db<>fiddle

Joining multiple tables and getting MAX value in subquery PostgreSQL

I have 4 Tables in PostgreSQL with the following structure as you can see below:
"Customers"
ID | NAME
101 Max
102 Peter
103 Alex
"orders"
ID | customer_id | CREATED_AT
1 101 2022-05-12
2 101 2022-06-14
3 101 2022-07-9
4 102 2022-02-14
5 102 2022-06-18
6 103 2022-05-22
"orderEntry"
ID | order_id | product_id |
1 3 10
2 3 20
3 3 30
4 5 20
5 5 40
6 6 20
"product"
ID | min_duration
10 P10D
20 P20D
30 P30D
40 P40D
50 P50D
Firstly I need to select "orders" with the max(created_at) date for each customer this is done with the query (it works!):
SELECT c.id as customerId,
o.id as orderId,
o.created_at
FROM Customer c
INNER JOIN Orders o
ON c.id = o.customer_id
INNER JOIN
(
SELECT customer_id, MAX(created_at) Max_Date
FROM Orders
GROUP BY customer_id
) res ON o.customer_id = res.customer_id AND
o.created_at = res.Max_date
the result will look like this:
customer_id | order_id | CREATED_AT
101 3 2022-07-9
102 5 2022-06-18
103 6 2022-05-22
Secondly I need to select for each order_id from "orderEntry" Table, "products" with the max(min_duration) the result should be:
order_id | max(min_duration)
3 P30D
5 P40D
6 P20D
and then join results from 1) and 2) queries by "order_id" and the total result which I'm trying to get should look like this:
customer_name | customer_id | Order_ID | Order_CREATED_AT | Max_Duration
Max 101 3 2022-07-9 P30D
Peter 102 5 2022-06-18 P40D
Alex 103 6 2022-05-22 P20D
I'm struggling to get query for 2) and then join everything with query from 1) to get the result. Any help I would appreciate!
You could make the first query to an CTE and use that to join the rest of the queries.
Like this.
WITH CTE AS ( SELECT c.id as customerId,
o.id as orderId,
o.created_at
FROM Customer c
INNER JOIN Orders o
ON c.id = o.customer_id
INNER JOIN
(
SELECT customer_id, MAX(created_at) Max_Date
FROM Orders
GROUP BY customer_id
) res ON o.customer_id = res.customer_id AND
o.created_at = res.Max_date)
SELECT customerId,orderId,created_at,p.min_duration
FROM CTE
JOIN (SELECT "orderId", MAX("product_id") as product_id FROM "orderEntry" GROUP BY orderId) oe ON CTE.orderId = oe.orderId
JOIN "product" pr ON oe.product_id = pr."ID"

How to aggregate among the values of different tables in a single query?

Tables Diagram
These are my datas from 3 tables.
select*from ShippingDetails
ProductCode Shipping Quantity
MFD01-10 50
MFD01-07 50
MFD01-10 10
select*from ProductDetails
ProductCode Shipping Quantity
MFD01-07 500
MFD01-10 100
MFD01-07 1000
MSD01-21 200
select*from StockData
ProductCode UrunAdi ( " Product Name")
MFD01-07 7 mm FTube
MFD01-10 10 mm FTube
MSD01-21 21 mm STube
MSD01-27 27 mm STube
I try to write these two queries but it didnt work. I couldn't merge as one table.
select StockData.ProductCode,SUM( ProductDetails.ProductQuantity) as ' Product Quantity' from ProductDetails RIGHT OUTER JOIN StockData on ProductDetails.ProductCode=StockData.ProductCode group by StockData.ProductCode
Product Code Product Quantity
MFD01-07 1500
MFD01-10 100
MSD01-21 200
MSD01-27 NULL
select StockData.ProductCode, SUM ( ShippingDetails.ShippingQuantity) as ' Shipping Quantity' from ShippingDetails RIGHT OUTER JOIN StockData on ShippingDetails.ProductCode=StockData.ProductCode group by StockData.ProductCode
Product Code Shipping Quantity
MFD01-07 50
MFD01-10 60
MSD01-21 NULL
MSD01-27 NULL
This result that i need. Which query would give it? I will appreciate if you solve my issue.
Product Code (Product-Shipping) Quantity
MFD01-07 1450
MFD01-10 40
MSD01-21 200
MSD01-27 NULL
You need a subquery for each table and then join together to calculate the total.
SQL Fiddle Demo
SELECT SD.[ProductCode],
COALESCE(PD.stock, 0) - COALESCE(D.sales, 0) as [(Product-Shipping) Quantity]
FROM StockData SD
LEFT JOIN (SELECT [ProductCode], SUM([Product Quantity]) stock
FROM ProductDetails
GROUP BY [ProductCode]) PD
ON SD.[ProductCode] = PD.[ProductCode]
LEFT JOIN (SELECT [ProductCode], SUM([Shipping Quantity]) sales
FROM ShippingDetails
GROUP BY [ProductCode]) D
ON SD.[ProductCode] = D.[ProductCode]
OUTPUT
| ProductCode | (Product-Shipping) Quantity |
|-------------|-----------------------------|
| MFD01-07 | 1450 |
| MFD01-10 | 40 |
| MSD01-21 | 200 |
| MSD01-27 | 0 |

SQL Server: SUM of a column based on another column

OrderDetailID OrderID ProductID Quantity
--------------------------------------------
1 10248 11 12
2 10248 42 10
3 10248 72 5
4 10249 14 9
5 10249 51 40
I need to get the total quantity based on the OrderID. So ideally the result should look like:
OrderID Quantity
----------------
10248 27
10249 49
I guess I have to group it by OrderID but not sure how to get the sum of quantity based on the OrderID.
Did you try using:
select OrderId, sum(Quantity) from yourtable group by OrderId
That should do it.