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.
Related
I have a simple Order table and one order can have different products with Quantity and it's Product's weight as below
OrderID
ProductName
Qty
Weight
101
ProductA
2
24
101
ProductB
1
24
101
ProductC
1
48
101
ProductD
1
12
101
ProductE
1
12
102
ProductA
5
60
102
ProductB
1
12
I am trying to partition and group the products in such a way that for an order, grouped products weight should not exceed 48.
Expected table look as below
OrderID
ProductName
Qty
Weight
GroupedID
101
ProductA
2
24
1
101
ProductB
1
24
1
101
ProductC
1
48
2
101
ProductD
1
12
3
101
ProductE
1
12
3
102
ProductA
4
48
1
102
ProductA
1
12
2
102
ProductB
1
12
2
Kindly let me know if this is possible.
Thank you.
This is a bin packing problem which is non-trivial in general. It's not just NP-complete but superexponential, ie the time increase as complexity increases is worse than exponential. Dai posted a link to Hugo Kornelis's article series which is referenced by everyone trying to solve this problem. The set-based solution performs really bad. For realistic scenarios you need iteration and preferably, using bin packing libraries eg in Python.
For production work it would be better to take advantage of SQL Server 2017+'s support for Python scripts and use a bin packing library like Google's OR Tools or the binpacking module. Even if you don't want to use sp_execute_external_script you can use a Python script to read the data from the database and split them.
The question's numbers are so regular though you could cheat a bit (actually quite a lot) and distribute all order lines into individual items, calculate the running total per order and then divide the total by the limit to produce the group number.
This works only because the running totals are guaranteed to align with the bin size.
Distributing into items can be done using a Tally/Numbers table, a table with a single Number column storing numbers from 0 to eg 1M.
Given the question's data:
declare #OrderItems table(id int identity(1,1) primary key, OrderID int,ProductName varchar(20),Qty int,Weight int)
insert into #OrderItems(OrderId,ProductName,Qty,Weight)
values
(101,'ProductA',2,24),
(101,'ProductB',1,24),
(101,'ProductC',1,48),
(101,'ProductD',1,12),
(101,'ProductE',1,12),
(102,'ProductA',5,60),
(102,'ProductB',1,12);
The following query will split each order item into individual items. It repeats each order item row as there are individual items and calculates the individual item weight
select o.*, Weight/Qty as ItemWeight
from #OrderItems o inner join Numbers ON Qty >Numbers.Number;
This row:
1 101 ProductA 2 24
Becomes
1 101 ProductA 2 24 12
1 101 ProductA 2 24 12
Calculating the running total inside a query can be done with :
SUM(ItemWeight) OVER(Partition By OrderId
Order By Itemweight
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
The Order By Itemweight claus means the smallest items are picked first, ie it's a Worst fit algorithm.
The overall query calculating the total and Group ID is
with items as (
select o.*, Weight/Qty as ItemWeight
from #OrderItems o INNER JOIN Numbers ON Qty > Numbers.Number
)
select Id,OrderId,ProductName,Qty,Weight, ItemWeight,
ceiling(SUM(ItemWeight) OVER(Partition By OrderId
Order By Itemweight
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)/48.0)
As GroupId
from items;
After that, individual items need to be grouped back into order items and groups. This produces the final query:
with items as (
select o.*, Weight/Qty as ItemWeight
from #OrderItems o INNER JOIN Numbers ON Qty > Numbers.Number
)
,bins as(
select Id,OrderId,ProductName,Qty,Weight, ItemWeight,
ceiling(SUM(ItemWeight) OVER(Partition By OrderId
Order By Itemweight
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)/48.0) As GroupId
from items
)
select
max(OrderId) as orderid,
max(productname) as ProductName,
count(*) as Qty,
sum(ItemWeight) as Weight,
max(GroupId) as GroupId
from bins
group by id,groupid
order by orderid,groupid
This returns
orderid
ProductName
Qty
Weight
GroupId
101
ProductA
2
24
1
101
ProductD
1
12
1
101
ProductE
1
12
1
101
ProductB
1
24
2
101
ProductC
1
48
3
102
ProductA
4
48
1
102
ProductA
1
12
2
102
ProductB
1
12
2
I have 3 tables.
For simplicity I changed them to these sample tables.
table1: CorporateActionSmmary
RATE Quantity ProductID
--------------------------
56 0 1487
30 0 1871
40 0 8750
table2# ProductMaster
RATEGROSS ISIN ProductID
--------------------------
60 JP0001 1487
33 JP0002 1871
45 JP0003 8750
table3# OpenPosition
Quantity ProductID
-------------------
5 1487
1 1487
5 1487
3 1871
2 1871
4 8750
2 8750
7 8750
3 8750
First I need to add ISIN from table2 to table1
table1: CorporateActionSmmary
RATE Quantity ProductID ISIN
-------------------------------------
56 0 1487 JP0001
30 0 1871 JP0002
40 0 8750 JP0003
So, I used this code
SELECT [dbo].[CorporateActionSummary].*, [dbo].[ProductMaster].[ISIN]
FROM [dbo].[CorporateActionSummary] JOIN [dbo].[ProductMaster] ON CorporateActionSummary.ProductID = ProductMaster.ProductID
Now as you can see the Quantity is missing in Table1 so I have to add-up all the quantities in Table3 for each product ID and add to Table1(as a new column or over-write the Quntity column)
I think I can get the sum of each ProductID's Quantity by the following code, But how can I add it to Table1 that already has ISIN column
SELECT SUM(Qantity),ProductID
FROM [dbo].[OpenPositions]
I am super new to SQL, please explain in detail if it is possible, thank you
I am using Microsoft SQL Server Management Studio
you can sum the quantities and then join with your query like so:
SELECT CA.*, PM.[ISIN],CA.Quantity
FROM [dbo].[CorporateActionSummary] CA
JOIN [dbo].[ProductMaster] PM
ON CA.ProductID = PM.ProductID
JOIN (
SELECT ProductID, SUM(Qantity) Quantity
FROM [dbo].[OpenPositions]
GROUP BY ProductID
) OO
on OO.ProductID = CA.ProductID
you are almost there.. you just need to use the same logic to join to the product master table. However, since you need the total of quantity, you need to group by the other columns you select (but not aggregate).
The query will be something like this :
SELECT
[dbo].[CorporateActionSummary].ProductID
, [dbo].[ProductMaster].[ISIN]
,sum([OpenPosition].Quantity) as quantity
FROM [dbo].[CorporateActionSummary]
JOIN [dbo].[ProductMaster]
ON CorporateActionSummary.ProductID = ProductMaster.ProductID
JOIN [dbo].[OpenPosition]
ON CorporateActionSummary.ProductID = OpenPosition.ProductID
group by
[dbo].[CorporateActionSummary].ProductID
, [dbo].[ProductMaster].[ISIN]
if you want to add more columns to your select, then you need to group by those colums as well
I have three columns within my table:
Amount, OrderNumber, Customerid
For each customerid there will be ordernumber and amount.
Now i need to display customerid,Ordernumber and Amount(total Amount- for each customerid).
custid srcnumber amount
112 4344 20
112 7678 10
112 8766 30
34 6577 15
34 4566 5
Expected:
custid srcnumber amount
112 4344 60
112 7678 60
112 8766 60
34 6577 20
34 4566 20
Use sum() over (partition by ..) analytic function to sum up the amount per each row :
select Customerid as custid,
OrderNumber as srcnumber,
sum(amount) over ( partition by Customerid ) as amount
from tab
order by custid desc
Demo
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
SQL appears to be more complex than I anticipated. My problem: for each customer, I would like to show the Customer ID and the total number of orders placed in 2011.
My table looks like this
Table: Order_t
Order_ID Order_Date Customer_ID
-------- ---------- -----------
1001 10/21/2011 1
1002 10/25/2011 8
1003 10/26/2011 15
1004 10/27/2011 5
1005 11/24/2011 3
1006 11/27/2011 2
1007 11/28/2011 11
1008 12/3/2011 12
1009 12/5/2011 1
1010 1/16/2012 4
I would like my query to display a table like this:
Customer_ID Orders_Placed
----------- -------------
1 2
2 1
3 1
5 1
8 1
11 1
12 1
15 1
My current query is this (I am currently completely neglecting the Date part because I haven't even figured out the grouping yet:
SELECT Customer_ID, SUM(Order_ID) AS Orders_Placed
FROM Order_t
GROUP BY Order_ID, Customer_ID
And this is my obviously wrong query:
Customer_ID Orders_Placed
----------- -------------
1 1001
8 1002
15 1003
5 1004
3 1005
2 1006
11 1007
12 1008
1 1009
4 1010
Thanks for help, but I would also like to understand where the problem is in my logic. What crucial part do I seem to not understand?
The problem with your logic is this
GROUP BY Order_ID, Customer_ID
Which means each combination of (Order_ID, Customer_ID) is put in a different GROUP. Since Order_ID alone is unique, practically no grouping is happening.
To do it correctly, you need to GROUP BY the Customer_ID (it reads like what you need, doesn't it), then COUNT the Orders. Finally, add the date filter as well.
SELECT Customer_ID, COUNT(Order_ID) AS Orders_Placed
FROM Order_t
WHERE Order_Date >= #1/1/2011# and Order_Date < #1/1/2012#
GROUP BY Customer_ID
Use count() instead
SELECT Customer_ID, COUNT(Order_ID) AS Orders_Placed
FROM Order_t
GROUP BY Customer_ID