How to Increment Total Sum/Count in Row SQL - sql

I am having trouble counting the TotalAmount incrementing by however many more number of policies there are iterating through each row.
For Example consider the following code:
SELECT
Customer.custno,
Customer.enteredDate AS 'Date Entered',
COUNT(BasicPolInfo.polid) AS 'Number of Policies',
SUM( COUNT(BasicPolInfo.polid)) over() AS TotalAmount
FROM Customer
INNER JOIN BasicPolInfo ON Customer.custid = BasicPolInfo.custid
WHERE BasicPolInfo.polid IS NOT NULL
and Customer.firstname IS NOT NULL
AND Customer.enteredDate > '1/1/79'
GROUP BY Customer.custno, Customer.firstname, Customer.lastname, Customer.entereddate
ORDER BY Customer.enteredDate ASC
What I would like to see is the TotalAmount Column be added from the Number of Policies iterating through each and every customer.
ex:
21 -- date -- 6 -- 6
24 -- date -- 13 -- 19
25 -- date -- 23 -- 32
29 -- date -- 16 -- 48
I could care less for the order of the custno, rather I am more concerned if the total policies are even 159703? There are more than 1000 rows in this SQL.
Please help me how I am able to sum each row from the preceding total sum!

In SQL Server 2012 forward you can use ROWS in an analytic/window function to get a running aggregate:
SELECT Customer.custno
, Customer.enteredDate AS 'Date Entered'
, COUNT(BasicPolInfo.polid) AS 'Number of Policies'
, SUM(COUNT(BasicPolInfo.polid)) OVER (ORDER BY Customer.custno ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ) AS TotalAmount
FROM Customer
INNER JOIN BasicPolInfo ON Customer.custid = BasicPolInfo.custid
WHERE BasicPolInfo.polid IS NOT NULL
AND Customer.firstname IS NOT NULL
AND Customer.enteredDate > '1/1/79'
GROUP BY Customer.custno
, Customer.firstname
, Customer.lastname
, Customer.entereddate
ORDER BY Customer.enteredDate ASC
Note that while you don't care about the order, an ORDER BY is required in order to determine which rows precede the current row.

It appears you are looking for a cumulative total.
This can be done via a CTE, joining the table on itself, a subquery or as of 2012 by using the "ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW" in the aggregate windowed function.
This can be done with any aggregated windowed function. You need to use
OVER (ORDER BY ______ ORDER BY ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
*Note you need to use order by to specify the arrangement of the column.
The below link to another stack overflow question provides some clear examples.
how to get cumulative sum

Related

Include Non-Existent Rows in Aggregate (Partition with Preceding and Following Rows)

I'm trying to get an average of the previous six months counts. However, I noticed if there are only 4 previous months, it will only do an average of those 4 months instead of 6 months. Is there a way to make so I forcefully sum over the 6 months?
select
ST.AccountNumber
, st.PrevMonth
, st.[Transaction Effective Date]
, st.[Transaction Amt]
, st.CurrentMonthTransCnt
, mt.EndOfMonth
, AvgMonthlyTransCntLast6Months = AVG(isnull(cnt, 0)) OVER (PARTITION BY MT.AccountNumber order by rowid ROWS BETWEEN 1 following and 6 following)
--into #AvgCntAndStdDev
from EDWAnalytics.ML.TEMP_SymitarTransactionsFinal as ST
left join MonthlyTransCnt as MT on MT.EndOfMonth = ST.PrevMonth and ST.AccountNumber = MT.AccountNumber
where ST.AccountNumber = '0000709510'
If you want the average of the previous six months for each month then the order should be inverted:
order by rowid desc
Another option is to use the current order, but with ROWS BETWEEN 6 preceding and 1 preceding
If you want the average to always be computed over six months you should replace avg with sum/6.
isnull(sum(cnt) OVER (PARTITION BY MT.AccountNumber order by rowid ROWS BETWEEN 6 preceding and 1 preceding),0)/6

ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW on sum column

how can i add
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW on sum column?
because the lead/lag function dont work
used the cod :
select year (orderdate),month (orderdate),sum (SubTotal),
lead ( sum (SubTotal),0) OVER ( PARTITION BY sum (SubTotal) ORDER BY sum (SubTotal) ) as PrevOrder
from sales.SalesOrderHeader
where orderdate <= '2011-12-31'
group by
grouping sets
((year (orderdate),month (orderdate)), () )
and add a name to the row total sum ?
added the expected output the table to extract the data and my query tablemy query
expected

How to create query for search total sales previous year

I have table named Sales.OrderValues that contain of 2 column, namely orderyear and val (total sales per day).
This is the record snippet (I cant show all of record because there are 830 rows)
I want to show the result like this
But, my output is different with my expected output.
As you can see, the expected output of prevtotalsales in 2008 is 618085.30. But, my output is 825169.29 (which is 208083.99 + 617085.30).
Below is my query
SELECT
YEAR(D1.orderdate) AS orderyear,
SUM(D1.val) AS curtotalsales,
(
SELECT
SUM(D2.val)
FROM
Sales.OrderValues D2
WHERE
YEAR(D1.orderdate) > YEAR(D2.orderdate)
)
AS prevtotalsales
FROM
Sales.OrderValues D1
GROUP BY
YEAR(D1.orderdate);
How to show the SUM of totalsales at the previous year without adding the next year's totalsales?
Basically, you want an equality condition in the WHERE clause of the subquery. This:
WHERE YEAR(D1.orderdate) > YEAR(D2.orderdate)
Should be:
WHERE YEAR(D1.orderdate) = YEAR(D2.orderdate) + 1
But it is much simpler and more efficient to just use lag():
SELECT
YEAR(orderdate) AS orderyear,
SUM(val) AS curtotalsales,
LAG(SUM(val)) OVER(ORDER BY YEAR(orderdate)) AS prevtotalsales
FROM Sales.OrderValues
GROUP BY YEAR(orderdate)
ORDER BY orderyear
You need to first SUM the values per year, and then use a cumulative SUM:
WITH Totals AS(
SELECT YEAR(OV.orderdate) AS OrderYear
SUM(OV.Val) AS YearSum
FROM Sales.OrderValues OV
GROUP BY YEAR(OV.orderdate))
SELECT OrderYear,
YearSum,
SUM(YearSum) OVER (ORDER BY OrderYear ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING) AS PreviousCumulative
FROM Totals;

Running total with Over

I'm trying to create a running total of the number of files per opened by day so I can use the data for a graph showing cumulative results.
The data is basically the file opening date, a calculated field showing 'This month' or 'Last Month' depending on the date and the running total field that I'm trying to figure out.
Date Month Count
==== ===== =====
2019-08-01 Last Month 6
2019-08-02 Last Month 2
2019-08-03 Last Month 5
I want to have a running total...so 6, 8, 13 etc
But all I'm getting is a row count (1,2,3 etc) for my count field.
select
FileDate,
Month,
sum(Count) OVER(PARTITION BY month order by Filedate) as 'Count'
from (
select
1 as 'Count',
Case
When month(cast(concat(right(d.var_val,4),substring(d.var_val,4,2),left(d.var_val,2)) as DATE) ) = Month(getdate()) then 'This Month'
else 'Last Month'
end as 'Month'
FROM data d
left join otherdata m on d.VAR_FileID = m.MAT_FileID
left join otherdata u on m.MAT_Fee_Earner = u.User_ID
left join otherdata br on m.MAT_BranchID = br.BR_ID
WHERE d.var_no IN ( '1628' )
and Len(var_val) = 10
)files
where Month(FileDate) in (MONTH(FileDate()),MONTH(getDate())-1)
and Year(Filedate) = Year(Getdate())
and Dept = 'Peterborough Property'
group by Month, FileDate, count
GO
I'm assuming I've not quite grasped the proper usage of 'OVER' - any pointers would be great!
The Partition clause indicates when to reset the count, so by partitioning by month you are only counting records for each discreet month to get a running total, over the whole dataset, you don't want the partition clause at all, just the order by clause.
Hope your clear with OVER clause now (with "Sentinel" answer), in which case you should replace desired column as follows, so that count continuously increase for all the rows from sub-query based on order by clause: for more details on OVER Clause..
sum(Count) OVER (Oder by Filedate) as [Count]
-- or
sum(Count) OVER (Oder by Filedate desc) as [Count]

Excluding rows from an AGG() OVER(ROWS BETWEEN x PRECEDING) if they're related to the current row?

I'm calculating a moving average of the last 100 sales of a particular item. I'd like to know if user X has spent more than 5 times everyone else combined, on that item in the last 100 sales window.
--how much has the current row user spent on this item over the last 100 sales?
SUM(saleprice) OVER(PARTITION BY item, user ORDER BY saledate ROWS BETWEEN 100 PRECEDING AND CURRENT ROW)
--pseudocode: how much has everyone else, excluding this user, spent on that item over the last 100 sales?
SUM(saleprice) OVER(PARTITION BY item ORDER BY saledate ROWS BETWEEN 100 PRECEDING AND CURRENT ROW WHERE preceding_row.user <> current_row.ruser)
Ultimately, I don't want the purchases made by my big spender to be factored into the total spend by the little spenders. Is there a technique that can exclude rows from a window, if they don't meet some comparison criteria versus the current row? (in my case, don't sum the saleprice from the preceding row if it bears the same user as the current row)
This first one looks fine to me, except you're counting 101 sales. (100 preceding AND the current row)
--how much has the current row user spent on this item over the last 100 sales?
SUM(saleprice)
OVER (
PARTITION BY item, user
ORDER BY saledate
ROWS BETWEEN 100 PRECEDING AND 1 PRECEDING -- 100 excluding this sale
ROWS BETWEEN 99 PRECEDING AND CURRENT ROW -- 100 including this sale
)
(Just use one of the two suggested ROWS BETWEEN clauses)
In the second expression, you can't add a WHERE clause. You can change the aggregation, the partition and the sorting, but I can't see how that would help you. I think you need a correlated sub-query and/or use of OUTER APPLY...
SELECT
*,
SUM(saleprice)
OVER (
PARTITION BY item, user
ORDER BY saledate
ROWS BETWEEN 99 PRECEDING AND CURRENT ROW -- 100 including this sale
)
AS user_total_100_purchases_to_date,
others_sales_top_100_total.sale_price
FROM
sales_data
OUTER APPLY
(
SELECT
SUM(saleprice) AS saleprice
FROM
(
SELECT TOP(100) saleprice
FROM sales_data others_sales
WHERE others_sales.user <> sales_data.user
AND others_sales.item = sales_data.item
AND others_sales.saledate <= sales_data.saledate
ORDER BY others_sales.saledate DESC
)
AS others_sales_top_100
)
AS others_sales_top_100_total
EDIT: Another way to look at it, to make things come consistent
SELECT
*,
usr_last100_saletotal,
all_last100_saletotal,
CASE WHEN usr_last100_saletotal > all_last100_saletotal * 0.8
THEN 'user spent 80%, or more, of last 100 sales'
ELSE 'user spent under 80% of last 100 sales'
END
AS
FROM
sales_data
OUTER APPLY
(
SELECT
SUM(CASE top100.user WHEN sales_data.user THEN top100.saleprice END) AS usr_last100_saletotal,
SUM( top100.saleprice ) AS all_last100_saletotal
FROM
(
SELECT TOP(100) user, saleprice
FROM sales_data AS others_sales
WHERE others_sales.item = sales_data.item
AND others_sales.saledate <= sales_data.saledate
ORDER BY others_sales.saledate DESC
)
AS top100
)
AS top100_summary