Adding a running total column in SQL Server - sql-server

I have a result set from a SQL Server query as below:
Item Week Amount
1 1 500
1 2 -100
1 3 -200
2 1 800
2 2 -600
2 3 -800
What I want to know is, is it possible to derive a fourth column that gives a running total of amount for each item?
My desired result is as follows:
Item Week Amount Total
1 1 500 500
1 2 -100 400
1 3 -200 200
2 1 800 800
2 2 -600 200
2 3 -800 -600
I am using SQL Server 2008

try SUM() OVER
SELECT
item,
week,
amount,
SUM(amount) over (partition by item order by Week) as Total
FROM yourTable

You will need a SQL Server 2012 to support ROWS PRECEDING functions.
SELECT
item,
week,
amount,
SUM(amount) over (partition by item order by Week
ROWS UNBOUNDED PRECEDING
) as Total
FROM yourTable
http://stevestedman.com/2012/03/rows-preceding-and-following-in-tsql-2012/

Related

How to fix Aggregation in Group By, missing aggregation values

I have a table of sales info, and am interested in Grouping by customer, and returning the sum, count, max of a few columns. Any ideas please.
I checked all the Select columns are included in the Group By statement, a detail is returned not the Groupings and aggregate values.
I tried some explicit naming but that didn't help.
SELECT
customerID AS CUST,
COUNT([InvoiceID]) AS Count_Invoice,
SUM([Income]) AS Total_Income,
SUM([inc2015]) AS Tot_2015_Income,
SUM([inc2016]) AS Tot_2016_Income,
MAX([prodA]) AS prod_A,
FROM [table_a]
GROUP BY
customerID, InvoiceID,Income,inc2015, inc2016, prodA
There are multiple rows of CUST, i.e. there should be one row for CUST 1, 2 etc.... it should say this...
---------------------------------------------
CUST Count_Invoice Total_Income Tot_2015_Income Tot_2016_Income prod_A
1 2 600 300 300 2
BUT IT IS RETURNING THIS
======================================
CUST Count_Invoice Total_Income Tot_2015_Income Tot_2016_Income prod_A
1 1 300 300 0 1
1 1 300 0 300 1
2 1 300 0 300 1
2 1 500 0 500 0
3 2 800 0 800 0
3 1 300 0 300 1
You don't need to group by other columns, since they are already aggregating by count, min, max or sum.
So you may try this
SELECT customerID as CUST
,count([InvoiceID]) as Count_Invoice
,sum([Income]) as Total_Income
,sum([inc2015]) as Tot_2015_Income
,sum([inc2016]) as Tot_2016_Income
,max([prodA]) as prod_A --- here you are taking Max but in output it seems like sum
FROM [table_a]
Group By customerID
Note: For column prod_A you are using max which gives 1 but in result it is showing 2 which is actually sum or count. Please check.
for more info you may find this link of Group by.
From the description of your expected output, you should be aggregating by customer alone:
SELECT
customerID A CUST,
COUNT([InvoiceID]) AS Count_Invoice,
SUM([Income]) AS Total_Income,
SUM([inc2015]) AS Tot_2015_Income,
SUM([inc2016]) AS Tot_2016_Income,
MAX([prodA]) AS prod_A
FROM [table_a]
GROUP BY
customerID;

TSQL Aggregating values based on other column

I have a table that looks as follows
Amount Factor Month Customer
1 1 2 A
3 1 2 A
4 -1 2 A
2 1 2 B
2 1 2 B
3 -1 2 B
4 1 3 A
5 1 3 A
6 -1 3 A
I want to aggregate (sum) the column Amount per Month and Customer. The Amounts should be multiplied with the value in the column Factor.
Hence, the result should look as follows (could be an UPDATE to the same table or a new table):
Amount Factor Month Customer
0 1 2 A
1 1 2 B
3 1 3 A
Try below
SELECT SUM(Amount * Factor) as Amount,Month,Customer
FROM tableName
GROUP BY Month,Customer
I think this is what you want:
select month, customer, sum(amount * factor) as sum_amount
from t
group by month, customer;
I'm not sure why you would want factor in the result set.

SQL Server sum column base on id

I have table and I want result like this:
id ItemId Quantity Sum
1 1 2 2
2 1 1 3
3 1 1 4
4 2 3 3
4 2 1 4
5 3 6 6
where id is primary key and ItemId is foreign key and Quantity is integer column.
I want that "sum" column sum quantity base on itemId.
I used group by but it does not give me what I want. With group by
I get this result:
id ItemId Quantity Sum
1 1 2 4
2 1 1 4
3 1 1 4
. . . .
. . . .
The "sum" column is same for all related rows.
I also used this code:
select it1.id, it1.ItemId, it1.Quantity ,
(select sum(it2.Quantity) from InventoryTransactions it2 where it2.ItemId = it1.ItemId and it2.id <= it1.Id) as Sum
from InventoryTransactions it1
order by it1.ItemId , it1.Id
This code gives me what I want but it takes too long for a large number of records, for example for one million records. It takes more than two minute to execute
What is the best way?
In sql server >= 2012 you can do a rolling sum easier
select it1.id, it1.ItemId, it1.Quantity,
sum(Quantity) over(partition by itemid order by Id rows unbounded preceding) as Sum
from InventoryTransactions it1
order by it1.ItemId , it1.Id

SQL Server: How to get a rolling sum over 3 days for different customers within same table

This is the input table:
Customer_ID Date Amount
1 4/11/2014 20
1 4/13/2014 10
1 4/14/2014 30
1 4/18/2014 25
2 5/15/2014 15
2 6/21/2014 25
2 6/22/2014 35
2 6/23/2014 10
There is information pertaining to multiple customers and I want to get a rolling sum across a 3 day window for each customer.
The solution should be as below:
Customer_ID Date Amount Rolling_3_Day_Sum
1 4/11/2014 20 20
1 4/13/2014 10 30
1 4/14/2014 30 40
1 4/18/2014 25 25
2 5/15/2014 15 15
2 6/21/2014 25 25
2 6/22/2014 35 60
2 6/23/2014 10 70
The biggest issue is that I don't have transactions for each day because of which the partition by row number doesn't work.
The closest example I found on SO was:
SQL Query for 7 Day Rolling Average in SQL Server
but even in that case there were transactions made everyday which accomodated the rownumber() based solutions
The rownumber query is as follows:
select customer_id, Date, Amount,
Rolling_3_day_sum = CASE WHEN ROW_NUMBER() OVER (partition by customer_id ORDER BY Date) > 2
THEN SUM(Amount) OVER (partition by customer_id ORDER BY Date ROWS BETWEEN 2 PRECEDING AND CURRENT ROW)
END
from #tmp_taml9
order by customer_id
I was wondering if there is way to replace "BETWEEN 2 PRECEDING AND CURRENT ROW" by "BETWEEN [DATE - 2] and [DATE]"
One option would be to use a calendar table (or something similar) to get the complete range of dates and left join your table with that and use the row_number based solution.
Another option that might work (not sure about performance) would be to use an apply query like this:
select customer_id, Date, Amount, coalesce(Rolling_3_day_sum, Amount) Rolling_3_day_sum
from #tmp_taml9 t1
cross apply (
select sum(amount) Rolling_3_day_sum
from #tmp_taml9
where Customer_ID = t1.Customer_ID
and datediff(day, date, t1.date) <= 3
and t1.Date >= date
) o
order by customer_id;
I suspect performance might not be great though.

Find and replace rows with similar value in one column in Oracle SQL

I want to find the rows which are similar to each other, and replace them with a new row. My table looks like this:
OrderID | Price | Minimum Number | Maximum Number | Volume
1 45 2 10 250
2 46 2 10 250
3 60 2 10 250
"Similar" in this context means that the rows that have same Maximum Number, Minimum Number, and Volume. Prices can be different, but the difference can be at most 2.
In this example, orders with OrderID of 1 and 2 are similar, but 3 is not (since even if it has same Minimum Number, Maximum Number, and Volume, its price is not within 2 units from orders 1 and 2).
Then, I want orders 1 and 2 be replaced by a new order, let's say OrderID 4, which has same Minimum Number and Maximum Number. Its Volume hass to be sum of volumes of the orders it is replacing. Its price can be the Price of any of the previous orders that will be deleted in the output table (45 or 46 in this example). So, the output for the example above would be:
OrderID | Price | Minimum Number | Maximum Number | Volume
4 45 2 10 500
3 60 2 10 250
Here is a way to do this in SQL Server 2012 or Oracle. The idea is to use lag() to find where groups should begin and end and then aggregate.
select min(id) as id, min(price) as price, MinimumNumber, MaximumNumber, sum(Volume)
from (select t.*,
sum(case when prev_price < price - 2 then 1 else 0 end) over
(partition by MinimumNumber, MaximumNumber, Volume order by price) as grp
from (select t.*,
lag(price) over (partition by MinimumNumber, MaximumNumber, Volume
order by price
) as prev_price
from table t
) t
) t
group by grp, price, MinimumNumber, MaximumNumber;
The only issue is the setting of the id. I'm not sure what the exact rule is for that.

Resources