How to calculate and update subtotal in SQL Server table - sql

I have a SQL Server table in following format: Type column is to indicate whether row is Subtotal or line level value.
If type is 1, then it is line level cost, and if type is 2, then it is subtotal and if type is 3, then it is grand total.
Category Subcategory Option1 Option2 Option3 Option4 Type
----------------------------------------------------------------------------
Insurance Insurance Cost 10 20 30 40 1
Insurance Insurance Tax 10 20 30 40 1
Insurance Subtotal 0 0 0 0 2
Finance Finance Cost 10 20 30 40 1
Finance Finance Tax 10 20 30 40 1
Finance Subtotal 0 0 0 0 2
GrandTotal GrandTotal 0 0 0 0 3
----------------------------------------------------------------------------
I want to update rows with subtotal with line level subtotal of respective category and grand total with line level total
Category Subcategory Option1 Option2 Option3 Option4 Type
----------------------------------------------------------------------------
Insurance Insurance Cost 10 20 30 40 1
Insurance Insurance Tax 10 20 30 40 1
Insurance Subtotal 20 40 60 80 2
Finance Finance Cost 10 20 30 40 1
Finance Finance Tax 10 20 30 40 1
Finance Subtotal 20 40 60 80 2
GrandTotal GrandTotal 40 80 120 160 3
----------------------------------------------------------------------------
How can I calculate and update these rows?
Awaiting for your reply.
Thanks.

I think cube extension for group by best suites for your case. Consider :
select distinct max(Category) as Category, Subcategory,
sum(Option1) as Option1
sum(Option2) as Option2,
sum(Option3) as Option3,
sum(Option4) as Option4
from t
where type = 1
group by cube(Category,Subcategory)
order by Category, Subcategory desc;
Rextester Demo "This is for Option1 column only, expandable for other options"
P.S. it's illogical to store a computable data in a table, that could already exist as basic data in that table as #Dougie pointed out.

You are trying to do an update, so that is a bit tricky. I think this will do the trick:
update t
set option1 = (case t.subcategory
when 'Subtotal' then subtotal.option1
when 'GrandTotal' then grandtotal.option1
else option1
end),
option2 = (case t.subcategory
when 'Subtotal' then subtotal.option2
when 'GrandTotal' then grandtotal.option2
else option2
end),
option3 = (case t.subcategory
when 'Subtotal' then subtotal.option3
when 'GrandTotal' then grandtotal.option3
else option3
end),
option4 = (case t.subcategory
when 'Subtotal' then subtotal.option4
when 'GrandTotal' then grandtotal.option4
else option4
end)
from t cross apply
(select category, sum(option1) as option1, sum(option2) as option2,
sum(option3) as option3)
from t t2
where t2.category = t.category
) subtotal cross join
(select category, sum(option1) as option1, sum(option2) as option2,
sum(option3) as option3)
from t t2
) grandtotal
where t.subcategory in ('Subtotal', 'GrandTotal');

Related

SQL group into ranges the sum of values

I have an table which contains inventory information for some items.
AGENT_ID
ITEM_ID
WAREHOUSE_ID
QTY
BATCH
AGE
100
IT101
1
10
B001
5
100
IT101
2
13
B002
8
100
IT101
1
15
B003
18
100
IT102
3
10
B005
42
The age column indicates how long the items had been in the warehouse. I need to generate an output which contains how the items had aged in the warehouse. These need to be summed into ranges based on the age. Below is an example output.
AGENT_ID
ITEM_ID
WAREHOUSE
R1(1-10)
R2(11-20)
R3(21<)
100
IT101
1
10
15
0
100
IT101
2
13
0
0
100
IT102
3
0
0
10
The first row in the second table represents that 10 items of IT101 had been in warehouse 1 for a period of (1-10) days, 15 items been in warehouse 1 for (11-20) days and so on.
Is there a way to group from agent, item, warehouse ids and sum the item quantity at the same time grouping it into ranges?
The first table may contain several millions of records. Tried this with some inner queries but it takes so much time.
you need to use conditional aggregation:
select AGENT_ID, ITEM_ID, WAREHOUSE
, sum(case when age between 1 and 10 then QTY end) "R1(1-10)"
, sum(case when age between 11 and 20 then QTY end) "R2(11-20)"
, sum(case when age > 20 then QTY end) "R3(21<)"
From table
group by AGENT_ID, ITEM_ID, WAREHOUSE

SQL - Case when product exists, fill up its corresponding value to all rows within the partition

I have the something like the following monthly data set.
I have a Product, company ID, Date, and Quantity. A company (denoted by Company ID) can buy multiple products. I want to create a new column that will have the quantity of Product 'C' if the company bought in the month at each line item. If Product 'C' is not bought, then return 0.
Product Company_ID Date Quantity Desired_Calculated_Column
A 1 5/1/2019 100 300
B 1 5/1/2019 200 300
C 1 5/1/2019 300 300
A 2 6/1/2019 150 125
B 2 6/1/2019 250 125
C 2 6/1/2019 125 125
A 3 7/1/2019 175 0
B 3 7/1/2019 275 0
I have been trying to partition the data based on Product and Company ID. I have been trying to leverage the LAST_VALUE but haven't been successful.
LAST_VALUE(quantity) OVER (PARTITION BY Date, Company_ID
ORDER BY product_group
) AS Desired_Calculated_Column
You don't want last_value(). You can use conditional aggregation, assuming that 'C' occurs once per group:
MAX(CASE WHEN product_group = 'C' THEN quantity ELSE 0 END) OVER
(PARTITION BY Date, Company_ID) AS C_quantity

How to classify or group values based on prior day values?

I have a data set that repeats daily and shows sales. If a product is released on Day 1 and has between 1-5 sales AND also if on Day 2 it has between 10-50 sales, I want to classify it as "Limited Sales."
If a product is released on Day 1 and has over 1,000 sales and also if on Day 2 it has over 1,000 sales, I want to classify it as "Wide Sales."
How would I go about doing this in standard SQL?
I've tried using some workarounds using CASE WHEN, but I ultimately end up with issues because while I can classify the 1st column with an output, I can't get the 2nd column to have an output that is also based on the 1st output (e.g. Column 1 is TRUE, but Column 2 is FALSE. What I need is for Column 1 = TRUE and Column 2 = True.
Here's what a sample query would look like:
Table looks like this:
Columns: name, day_number, sales
1. Jack | 1 | 5
2. Jack | 2 | 10
3. Mary | 1 | 1250
4. Mary | 2 | 1500
SELECT name,
day_number,
sales,
CASE
WHEN day_number = 1
AND sales >= 1
AND sales <= 5
THEN "LIMITED SALES"
ELSE "WIDE SALES"
END AS status_1,
CASE
WHEN day_number = 2
AND sales >= 10
AND sales <= 50
THEN TRUE
ELSE FALSE
END AS status_2
FROM table
Unfortunately this isn't really going to get me what I want. At the end of the day, I would like to see results like:
1. Jack | 1 | 5 | LIMITED SALES
2. Jack | 2 | 10 | LIMITED SALES
3. Mary | 1 | 1250 | WIDE SALES
4. Mary | 2 | 1500 | WIDE SALES
Is this what you want?
select name,
(case when sum(case when day_number = 1 then sales end) between 1 and 5 and
sum(case when day_number = 2 then sales end) between 10 and 50
then 'Limited Sales'
when sum(case when day_number = 1 then sales end) > 1000 and
sum(case when day_number = 2 then sales end) > 1000
then 'Wide Sales'
else '???'
end) as sales_category
from t
group by name
If you want this on each of the original rows, then use window functions or a join.

How to implement a stored procedure for my case?

I will implement a SSRS summary data report with following data records from one table.
ID Part Type Value
------------------------------------------------------------
1 Payroll State Tax 2010
1 Payroll City Tax 500
1 Payroll Medical 300
2 Payroll State Tax 2000
2 Payroll City Tax 400
3 Payroll FICA 200
1 Refund State Tax -500
1 Refund Medical -100
3 Payroll FICA 200
1 Refund State Tax -500
1 Refund Medical -100
How do I implement a stored procedure to produce following result by summing same Type values for each Part so it is easy to build the SSRS report? Thanks a lot!
Type Payroll Refund Total
State Tax 4010 -500 3600
City Tax 900 0 900
FICA 400 0 400
Medical 300 -100 200
You can use a CTE/subquery with conditional aggregation as
CREATE PROCEDURE YourProcName
AS
SET NOCOUNT ON;
WITH CTE AS
(
SELECT Type,
SUM(CASE WHEN Value > 0 THEN Value ELSE 0 END) payroll,
SUM(CASE WHEN Value < 0 THEN Value ELSE 0 END) refund
FROM YourTable
GROUP BY Type
)
SELECT CTE.*,
CTE.payroll + CTE.refund Total
FROM CTE;
GO;
You can use conditional aggregation:
select type,
sum(case when part = 'Payroll' then value else 0 end) as payroll,
sum(case when part = 'Refund' then value else 0 end) as refund
from t
group by type;
Why would you want a stored procedure when a simple query does what you want? I would strongly recommend a view or table-valued function instead.

SQL Group by range and total column

With the following function and stored procedure i get a resultset with 2 columns.
What i need additional is a third column total for all open invoices inner score range
It would great for any idea.
ALTER FUNCTION dbo.OpenOrders
(
#MandantId int
)
RETURNS TABLE
AS
RETURN
SELECT SUM(Invoices.Amount - ISNULL(Payment.Amount, 0)) AS Op
FROM Invoices INNER JOIN
Orders ON Invoices.OrderId = Orders.OrderId LEFT OUTER JOIN
Payment ON Invoices.InvoiceId = Payment.InvoiceId
WHERE (Orders.MandantId = #MandantId)
GROUP BY Invoice.InvoiceId, Invoices.Amount
HAVING (SUM(Invoices.Amount - ISNULL(Payment.Amount, 0)) <> 0)
ALTER PROCEDURE dbo.GetOpRanges
#MandantId int
AS
BEGIN
SELECT * INTO #tmp_ranges
FROM
//wrong in first post -> OPDebitorByMandant(#MandantId)
OpenOrders(#MandantId)
SELECT op AS [score range], COUNT(*) AS [number of occurences]
FROM
(SELECT CASE
WHEN op BETWEEN 0 AND 50 THEN ' 0- 50'
WHEN op BETWEEN 50 AND 100 THEN ' 50-100'
WHEN op BETWEEN 100 AND 500 THEN '100-500'
ELSE '500 and >' END AS op
FROM [#tmp_ranges]) AS t
GROUP BY op
RETURN
Result:
score range number of occurences range
------------------+-------------
0- 50 23
50-100 4
100-500 4
500 and > 21
What i need additional is a third column total for all open invoices inner score range.
Target result:
score range number of occurences Total
-----------+--------------------+------
0- 50 23 1.150
50-100 4 400
100-500 4 2.000
500 and > 21 22.000
Tables:
Invoices
InvoiceId CustomerId OrderId DateOfInvoice Amount
----------+----------+-------+-------------+------
1 1 20 20160301 1000.00
2 2 22 20160501 2000.00
3 1 102 20160601 3000.00
...
Orders
OrderId MandantId CustomerId DateOfOrder Amount
-------+---------+----------+-----------+-----------
20 1 1 20160101 1000.00
22 1 2 20160101 2000.00
102 1 1 20160101 3000.00
...
Payment
PaymentId MandantId CustomerId InvoiceId OrderId DateOfPayment Amount
---------+---------+----------+---------+-------+-------------+-------------
1 1 1 1 20 20160310 1000.00
2 1 2 2 22 20160505 2000.00
3 1 1 3 102 20160610 3000.00
...
hope it's helpfull and thanks again in advance for any solution