How to sum up Loss amount per each claim ignoring date - sql

I have table with Loss amount per each transaction date.
How can I create column ClaimLoss that would sum up Loss amount per each claim?
declare #TempTable1 table (ID int, ClaimNumber varchar(100), date date, Loss money)
insert into #TempTable1
values (1, 'Claim1','2017-01-01', 100),
(2, 'Claim1','2017-03-06',150),
(3, 'Claim1','2017-05-01', 50),
(4, 'Claim2','2018-01-01', 150),
(5, 'Claim2','2018-08-15', 250),
(6, 'Claim2','2018-05-03', 350),
(7, 'Claim3','2018-09-01', 330),
(8, 'Claim4','2019-01-01', 140),
(9, 'Claim4','2019-01-13', 225),
(10, 'Claim5','2019-02-01', 145)
select ID,
ClaimNumber,
Date,
Loss
from #TempTable1
I need something like this:
Is it possible to do in the same select statement?

This seems like a place to use row_number() and case:
select t.*,
(case when row_number() over (partition by ClaimNumber order by date) = 1
then sum(loss) over (partition by ClaimNumber)
else 0
end) as claimloss
from #TempTable1 t;

You can use window function:
select ID, ClaimNumber, Date, Loss,
(case when min(id) over (partition by ClaimNumber) = id
then sum(loss) over (partition by ClaimNumber)
else 0
end) as claimloss
from #TempTable1;

Related

How to write a running total based on criteria in T-SQL

I'm building a report which gives me the total count of unique accounts within a calendar month.
However, this total is based on the number of active accounts (accounts subscribed to a service), and once their contract ends they will be excluded from the total count.
For example, Company A has subscribed to the service on 1/1/2018 and their contract ends on 1/1/2020. So Company A should be included in the total count of unique accounts for all the months their under contract until their contract ends.
End Result would look something like this:
Here is the SQl query that I have so far. How can I write the code such that it will give me this cumulative/running total. I added the columns for reference.
SELECT A.Name, CA.Name, CA.Start_Date__c, CA.End_Date__c, CA.Product_Code_CPQ__c
FROM [salesforce].[Client_Asset__c] AS CA
INNER JOIN salesforce.Account AS A
ON CA.Account__c = A.Id
WHERE Product_Code_CPQ__c IN(
'DSWPSTRSUB','DSWPESSSUB','DSWPPROSUB','DSWPHOSTSUB','DSWPMULTIHOSTSUB','DSWPOLXWRAPFPE',
'DSWPOLXWRAPSUB','WPCALENDARFORALT','WPCALHOSTINGBUN','IMWPTM','SBWPRET','SBWPRETNR','WORDPLUMWEBSUCCESS',
'WORDPWEBSUCCESS','WORDPOGS','FDSTRWORDPDESGNSUB','FDWPFPE','WORDPEMERGHOST','WORDPSUBBUN','WPOLXPLUGIN',
'POSTSTARTWORDPAF','POSTWORDPSTARTBUN','LUMWORDPSSUBBUN','WORDPLUMOGS','LUMFDSTRWPDESGNSUB',
'LUMPSTWORDPSTRBUN','LUMPOSTSTRTWORDPAF','FDWPEMERGFPE')
AND End_Date__c > GETDATE()
AND Active__c = 1
Try something like that:
CREATE TABLE #tmp ([month] INT, [group] VARCHAR(10), [value] REAL)
INSERT INTO #tmp ([month], [group], [value]) VALUES
(1, 'A', 1), (2, 'A', 5), (3, 'A', 3), (4, 'A', 2), (5, 'A', 8),
(1, 'B', 7), (2, 'B', 3), (3, 'B', 2), (4, 'B', 4), (5, 'B', 6)
SELECT c.[month], c.[group], c.current_total, r.running_total
FROM
(
SELECT [month],[group], SUM([value]) current_total
FROM #tmp
GROUP BY [month],[group]
) C JOIN
(
SELECT [month],[group], SUM([value]) OVER (partition BY [group] ORDER BY [month]) running_total
FROM #tmp
) R ON C.[month]=R.[month] AND C.[group]=R.[group]
ORDER BY 2,1
Tested on mssql 2016. Handle potential missing values yourself.

Update same data from the same table

I have a table that has registration of several CAR (repeated and not repeated), so I intend to update a field (active) of all repeated registrations leaving 1 more recent line. Exemple of table data:
I want my data to be like this:
I tried to make this code, but it is not working correctly. Because updated the first row
-----create table
create table dbo.test( id int, CAR varchar(30), ACTIVE int)
insert into dbo.test(id, CAR, ACTIVE)
values
(1, 'AAA-25-35', 0),
(2, 'LDB-25-35', 0),
(3, 'LDB-00-35', 0),
(4, 'LDB-25-35', 0),
(5, 'LDB-00-35', 0),
(6, 'LDC-10-10', 0),
(7, 'LDC-10-10', 0),
(8, 'LDB-00-35', 0)
select * from dbo.test
----update table
WITH CTE AS
(
SELECT
row_number() over (partition by CAR order by id) as t
, CAR , ACTIVE
FROM dbo.test
)
update CTE
SET ACTIVE = 1
WHERE t=1
select * from dbo.test
You could just add an EXISTS portion:
-----create table
CREATE TABLE #Test(ID INT, CAR VARCHAR(30), ACTIVE INT)
INSERT INTO #Test(ID, CAR, ACTIVE)
VALUES
(1, 'AAA-25-35', 0),
(2, 'LDB-25-35', 0),
(3, 'LDB-00-35', 0),
(4, 'LDB-25-35', 0),
(5, 'LDB-00-35', 0),
(6, 'LDC-10-10', 0),
(7, 'LDC-10-10', 0),
(8, 'LDB-00-35', 0)
----update table
;WITH CTE AS
(
SELECT ROW_NUMBER() OVER(PARTITION BY CAR ORDER BY ID) AS t,
CAR,
ACTIVE
FROM #Test
)
UPDATE CTE
SET ACTIVE = 1
WHERE t=1
AND EXISTS (SELECT 1 FROM CTE c WHERE c.CAR = CTE.CAR GROUP BY CAR HAVING COUNT(*) > 1)
SELECT *
FROM #Test
You can use count analytical function in your query as follows:
WITH CTE AS
(
SELECT
row_number() over (partition by CAR order by id) as t
, count(*) over (partition by car) as cnt
, CAR , ACTIVE
FROM dbo.test
)
update CTE
SET ACTIVE = 1
WHERE t=1 and cnt>1

Update same data from the same table depending on a column

I have a similar case of a table, Like the case of this link Update same data from the same table
but in my case it must update depending on the column "dependency". In other words, it updates the repetitions in the tables always leaving the most recent line and for table that only have one line it does not update. My data is like this:
I want it to be updated like this:
I tryed this code:
create table dbo.test( id int, CAR varchar(30), ACTIVE int, dependency int)
insert into dbo.test(id, CAR, ACTIVE, dependency)
values
(1, 'AAA-25-35', 0,1),
(2, 'LDB-25-35', 0,2),
(3, 'LDB-00-35', 0,2),
(4, 'LDB-25-35', 0,2),
(5, 'LDB-00-35', 0,2),
(6, 'LDC-10-10', 0,2),
(7, 'LDC-10-10', 0,2),
(8, 'LDB-00-35', 0,2),
(9, 'AAA-25-35', 0,1),
(10, 'AAA-25-35', 0,3),
(11, 'AAA-25-35', 0,3),
(12, 'BBB-25-35', 0,2),
(13, 'BBB-25-35', 0,3),
(14, 'BBB-25-35', 0,3)
GO
SELECT * FROM TEST
WITH CTE AS
(
SELECT ROW_NUMBER() OVER(PARTITION BY CAR ORDER BY ID) AS t,
CAR,
ACTIVE
FROM Test
)
UPDATE CTE
SET ACTIVE = 1
WHERE t=1
AND EXISTS (SELECT 1 FROM CTE c WHERE c.CAR = CTE.CAR GROUP BY CAR HAVING COUNT(*) > 1)
go
SELECT * FROM test
Try changing the SELECT and WHERE clauses:
WITH CTE AS (
SELECT ROW_NUMBER() OVER(PARTITION BY CAR, dependency ORDER BY ID) AS t,
LEAD(id) OVER (PARTITION BY CAR, dependency ORDER BY ID) as next_id,
CAR,
ACTIVE
FROM Test
)
UPDATE CTE
SET ACTIVE = 1
WHERE t = 1 AND next_id IS NOT NULL

Create range of ID without cursor

Not sure if this or similar question is asked already but i could not find one.
The Requirement to create range of IDs while the Value is not changed. This schema can be used:
declare #mytable as table(ID int, Val int)
insert into #mytable values
(1, 1),
(2, 1),
(3, 1),
(4, 2),
(5, 2),
(6, 2),
(7, 2),
(8, 1),
(9, 1),
(10, 1),
(11, 4),
(12, 4),
(13, 4),
(14, 4),
(15, 4),
(16, 5);
And the expected result would be
StartID EndID Val
1 3 1
4 7 2
8 10 1
11 15 4
16 16 5
Now I can achieve this by running cursor and If n case the number of records will be millions, I think, cursor will be slower. I hope it can be written using some compound query but could not figure-out how.
So I need help in writing that kind of query and needless to mention yet, it is not a school/collage project/assignment.
This is a gaps-and-islands scenario where you're trying to group records together based on the change in Val.
This is using window functions to determine when the Val changes, and assign the island_nbr.
Answer:
select min(b.ID) as StartID
, max(b.ID) as EndID
, max(b.Val) as Val
from (
select a.ID
, a.Val
, sum(a.is_chng_flg) over (order by a.ID asc) as island_nbr
from (
select m.ID
, m.Val
, case lag(m.Val, 1, m.Val) over (order by m.ID asc) when m.Val then 0 else 1 end is_chng_flg
from #mytable as m
) as a
) as b
group by b.island_nbr --forces the right records to show up
order by 1
This is a gaps-and-islands problem. But the simplest method is the difference of row numbers:
select min(id) as startId, max(id) as endId, val
from (select t.*,
row_number() over (order by id) as seqnum,
row_number() over (partition by val order by id) as seqnum_v
from #mytable t
) t
group by (seqnum - seqnum_v), val
order by startId;

Multiple SQL MAX when items are not in order

I have some data as below:
DECLARE #MyTable AS TABLE
(productName varchar(13), test1 int,test2 int)
INSERT INTO #MyTable
(productName, test1,test2)
VALUES
('a', 1,1),
('a', 2,2),
('a', 3,3),
('b', 1,4),
('b', 2,5),
('b', 3,6),
('a', 1,7),
('a', 4,8),
('a', 5,9)
;
SELECT productname,MAX(test1) from #MyTable group BY productname
a MAX query on test1 column gives
a,5
b,3
but I need to have result as
a,3
b,3
a,5
when I have order by test2
You can solve this by using a trick with row_numbers, so that you assign 2 different row numbers, one for the whole data and one that is partitioned by productname. If you compare the difference between these numbers, you can figure out when product name has changed, and use that to determine the max values for each group.
select productname, max(test1) from (
SELECT *,
row_number() over (order by test2 asc) -
row_number() over (partition by productname order by test2 asc) as GRP
from #MyTable
) X
group by productname, GRP
You can test this in SQL Fiddle
If the test2 column is always a row number without gaps, you can use that too instead of the first row number column. If you need ordering in the data, you'll have to for example to use the max of test1 to do that.
Please check the following SQL Select statement
DECLARE #MyTable AS TABLE (productName varchar(13), test1 int,test2 int)
INSERT INTO #MyTable
(productName, test1,test2)
VALUES
('a', 1,1),
('a', 2,2),
('a', 3,3),
('b', 1,4),
('b', 2,5),
('b', 3,6),
('a', 1,7),
('a', 4,8),
('a', 5,9)
DECLARE #MyTableNew AS TABLE (id int identity(1,1), productName varchar(13), test1 int,test2 int)
insert into #MyTableNew select * from #MyTable
--select * from #MyTableNew
;with cte as (
SELECT
id, productName, test1, test2,
case when (lag(productName,1,'') over (order by id)) = productName then 0 else 1 end ischange
from #MyTableNew
), cte2 as (
select t.*,(select sum(ischange) from cte where id <= t.id) grp from cte t
)
select distinct grp, productName, max(test1) over (partition by grp) from cte2
This is implemented according to the following SQL Server Lag() function tutorial
The Lag() function is used to identify and order the groups in table data
Please try this query
DECLARE #MyTable AS TABLE
(productName varchar(13), test1 int,test2 int)
INSERT INTO #MyTable
(productName, test1,test2)
VALUES
('a', 1,1),
('a', 2,2),
('a', 3,3),
('b', 1,4),
('b', 2,5),
('b', 3,6),
('a', 1,7),
('a', 4,8),
('a', 5,9)
;
SELECT productname,MAX(test1)
from #MyTable
where test1 = test2
group BY productname
union all
SELECT productname,MAX(test1)
from #MyTable
where test1 != test2
group BY productname