SQL: Pinned rows and row number calculation - sql

We have a requirement to assign row number to all rows using following rule
Row if pinned should have same row number
Otherwise sort it by GMD
Example:
ID GMD IsPinned
1 2.5 0
2 0 1
3 2 0
4 4 1
5 3 0
Should Output
ID GMD IsPinned RowNo
5 3 0 1
2 0 1 2
1 2.5 0 3
4 4 1 4
3 2 0 5
Please Note row number for Id's 2 and 4 stayed intact as they are pinned with values of 2 and 4 respectively even though the GMD are not in any order
Rest of rows Id's 1, 3 and 5 row numbers are sorted using GMD desc
I tried using RowNumber SQL 2012 however, it is pushing pinned items from their position

Here's a set-based approach to solving this. Note that the first CTE is unnecessary if you already have a Numbers table in your database:
declare #t table (ID int,GMD decimal(5,2),IsPinned bit)
insert into #t (ID,GMD,IsPinned) values
(1,2.5,0), (2, 0 ,1), (3, 2 ,0), (4, 4 ,1), (5, 3 ,0)
;With Numbers as (
select ROW_NUMBER() OVER (ORDER BY ID) n from #t
), NumbersWithout as (
select
n,
ROW_NUMBER() OVER (ORDER BY n) as rn
from
Numbers
where n not in (select ID from #t where IsPinned=1)
), DataWithout as (
select
*,
ROW_NUMBER() OVER (ORDER BY GMD desc) as rn
from
#t
where
IsPinned = 0
)
select
t.*,
COALESCE(nw.n,t.ID) as RowNo
from
#t t
left join
DataWithout dw
inner join
NumbersWithout nw
on
dw.rn = nw.rn
on
dw.ID = t.ID
order by COALESCE(nw.n,t.ID)
Hopefully my naming makes it clear what we're doing. I'm a bit cheeky in the final SELECT by using a COALESCE to get the final RowNo when you might have expected a CASE expression. But it works because the contents of the DataWithout CTE is defined to only exist for unpinned items which makes the final LEFT JOIN fail.
Results:
ID GMD IsPinned RowNo
----------- --------------------------------------- -------- --------------------
5 3.00 0 1
2 0.00 1 2
1 2.50 0 3
4 4.00 1 4
3 2.00 0 5
Second variant that may perform better (but never assume, always test):
declare #t table (ID int,GMD decimal(5,2),IsPinned bit)
insert into #t (ID,GMD,IsPinned) values
(1,2.5,0), (2, 0 ,1), (3, 2 ,0), (4, 4 ,1), (5, 3 ,0)
;With Numbers as (
select ROW_NUMBER() OVER (ORDER BY ID) n from #t
), NumbersWithout as (
select
n,
ROW_NUMBER() OVER (ORDER BY n) as rn
from
Numbers
where n not in (select ID from #t where IsPinned=1)
), DataPartitioned as (
select
*,
ROW_NUMBER() OVER (PARTITION BY IsPinned ORDER BY GMD desc) as rn
from
#t
)
select
dp.ID,dp.GMD,dp.IsPinned,
CASE WHEN IsPinned = 1 THEN ID ELSE nw.n END as RowNo
from
DataPartitioned dp
left join
NumbersWithout nw
on
dp.rn = nw.rn
order by RowNo
In the third CTE, by introducing the PARTITION BY and removing the WHERE clause we ensure we have all rows of data so we don't need to re-join to the original table in the final result in this variant.

this will work:
CREATE TABLE Table1
("ID" int, "GMD" number, "IsPinned" int)
;
INSERT ALL
INTO Table1 ("ID", "GMD", "IsPinned")
VALUES (1, 2.5, 0)
INTO Table1 ("ID", "GMD", "IsPinned")
VALUES (2, 0, 1)
INTO Table1 ("ID", "GMD", "IsPinned")
VALUES (3, 2, 0)
INTO Table1 ("ID", "GMD", "IsPinned")
VALUES (4, 4, 1)
INTO Table1 ("ID", "GMD", "IsPinned")
VALUES (5, 3, 0)
SELECT * FROM dual
;
select * from (select "ID","GMD","IsPinned",rank from(select m.*,rank()over(order by
"ID" asc) rank from Table1 m where "IsPinned"=1)
union
(select "ID","GMD","IsPinned",rank from (select t.*,rank() over(order by "GMD"
desc)-1 rank from (SELECT * FROM Table1)t)
where "IsPinned"=0) order by "GMD" desc) order by rank ,GMD;
output:
2 0 1 1
5 3 0 1
1 2.5 0 2
4 4 1 2
3 2 0 3

Can you try this query
CREATE TABLE Table1
(ID int, GMD numeric (18,2), IsPinned int);
INSERT INTO Table1 (ID,GMD, IsPinned)
VALUES (1, 2.5, 0),
(2, 0, 1),
(3, 2, 0),
(4, 4, 1),
(5, 3, 0)
select *, row_number () over(partition by IsPinned order by (case when IsPinned =0 then GMD else id end) ) [CustOrder] from Table1

This took longer then I thought, the thing is row_number would take a part to resolve the query. We need to differentiate the row_numbers by id first and then we can apply the while loop or cursor or any iteration, in our case we will just use the while loop.
dbo.test (you can replace test with your table name)
1 2.5 False
2 0 True
3 3 False
4 4 True
6 2 False
Here is the query I wrote to achieve your result, I have added comment under each operation you should get it, if you have any difficultly let me know.
Query:
--user data table
DECLARE #userData TABLE
(
id INT NOT NULL,
gmd FLOAT NOT NULL,
ispinned BIT NOT NULL,
rownumber INT NOT NULL
);
--final result table
DECLARE #finalResult TABLE
(
id INT NOT NULL,
gmd FLOAT NOT NULL,
ispinned BIT NOT NULL,
newrownumber INT NOT NULL
);
--inserting to uer data table from the table test
INSERT INTO #userData
SELECT t.*,
Row_number()
OVER (
ORDER BY t.id ASC) AS RowNumber
FROM test t
--creating new table for ids of not pinned
CREATE TABLE #ids
(
rn INT,
id INT,
gmd FLOAT
)
-- inserting into temp table named and adding gmd by desc
INSERT INTO #ids
(rn,
id,
gmd)
SELECT DISTINCT Row_number()
OVER(
ORDER BY gmd DESC) AS rn,
id,
gmd
FROM #userData
WHERE ispinned = 0
--declaring the variable to loop through all the no pinned items
DECLARE #id INT
DECLARE #totalrows INT = (SELECT Count(*)
FROM #ids)
DECLARE #currentrow INT = 1
DECLARE #assigningNumber INT = 1
--inerting pinned items first
INSERT INTO #finalResult
SELECT ud.id,
ud.gmd,
ud.ispinned,
ud.rownumber
FROM #userData ud
WHERE ispinned = 1
--looping through all the rows till all non-pinned items finished
WHILE #currentrow <= #totalrows
BEGIN
--skipping pinned numers for the rows
WHILE EXISTS(SELECT 1
FROM #finalResult
WHERE newrownumber = #assigningNumber
AND ispinned = 1)
BEGIN
SET #assigningNumber = #assigningNumber + 1
END
--getting row by the number
SET #id = (SELECT id
FROM #ids
WHERE rn = #currentrow)
--inserting the non-pinned item with new row number into the final result
INSERT INTO #finalResult
SELECT ud.id,
ud.gmd,
ud.ispinned,
#assigningNumber
FROM #userData ud
WHERE id = #id
--going to next row
SET #currentrow = #currentrow + 1
SET #assigningNumber = #assigningNumber + 1
END
--getting final result
SELECT *
FROM #finalResult
ORDER BY newrownumber ASC
--dropping table
DROP TABLE #ids
Output:

Related

how to pick top 2 rows in a table based on the indicator

I have a sample data like this
Declare #table Table
(
ID INT,
Value VARCHAR(10),
Is_failure int
)
insert into #table(ID, Value, Is_failure) values (1, 'Bits', 0)
insert into #table(ID, Value, Is_failure) values (2, 'Ip', 0)
insert into #table(ID, Value, Is_failure) values (3, 'DNA', 0)
insert into #table(ID, Value, Is_failure) values (6, 'DCP', 1)
insert into #table(ID, Value, Is_failure) values (8, 'Bits', 0)
insert into #table(ID, Value, Is_failure) values (11, 'calc', 0)
insert into #table(ID, Value, Is_failure) values (14, 'DISC', 0)
insert into #table(ID, Value, Is_failure) values (19, 'DHCP', 1)
Looks like this:
ID Value Is_failure
1 Bits 0
2 Ip 0
3 DNA 0
6 DCP 1
8 Bits 0
11 calc 0
14 DISC 0
19 DHCP 1
Data continuous like this ... I need to fetch top 2 records along with Is_failure whenever Is_failure = 1 comes if it is 0 no need to pick up .
Sample output:
ID Value Is_failure
2 Ip 0
3 DNA 0
6 DCP 1
11 calc 0
14 DISC 0
19 DHCP 1
Suggest on this I have tried with having count(*) and other things but not fruitful.
You can use this query
Declare #tmptable Table
(
ID INT,
Value VARCHAR(10),
Is_failure int,
rowNum int
)
Declare #continuousRows int =2
insert into #tmptable
select *,ROW_NUMBER() over (order by id) from #table
;with cte1 as
(select *
from #tmptable t
where (select sum(Is_failure) from #tmptable t1 where t1.rowNum between t.rowNum-#continuousRows and t.rowNum
having count(*)=#continuousRows+1)=1
and t.Is_failure=1
)
,cte2 as
(
select t.* from #tmptable t
join cte1 c on t.rowNum between c.rowNum-#continuousRows and c.rowNum
)
select c.ID,value,Is_failure from cte2 c
You can use window functions for this:
select id, value, is_failure
from (select t.*,
lead(Is_failure) over (order by id) as next_if,
lead(Is_failure, 2) over (order by id) as next_if2
from #table t
) t
where 1 in (Is_failure, next_if, next_if2)
order by id;
You can simplify this with a windowing clause:
select id, value, is_failure
from (select t.*,
max(is_failure) over (order by id rows between current row and 2 following) as has_failure
from #table t
) t
where has_failure > 0
order by id;

I want to insert values for Ranks column on bases of other column required condition

declare #count int = 0
while (#count != 3156)
begin
while(ErrorCode not like 'm%')
begin
insert into #temp(Ranks)
values(#count)
end
set #count = #count + 1
end
I have a temp table with 3 columns ErrorCode, ErrorCount and Ranks and I have to insert same values in Ranks column whenever initial value of ErrorCode is repeated. I have NULL in Ranks column now:
ErrorCode ErrorCount Ranks
----------------------------------------------------
module_position A1_16__1 1
head_id HZ0C1 000877 1
start_dt 2018-09-10 00:18:27 1
module_position A2_16__1 2
head_id HZ0C1 000878 2
start_dt 2018-09-10 00:18:27 2
I want output as above for the Ranks column. Note, that the initial value is always module_position whenever I have to insert new rank.
Try this:
DECLARE #DataSource TABLE
(
[RowID] INT
,[ErrorCode] VARCHAR(32)
,[ErrorCount] VARCHAR(32)
);
INSERT INTO #DataSource ([RowID], [ErrorCode], [ErrorCount])
VALUES (1, 'module_position', 'A1_16__1')
,(2, 'head_id HZ0C1', ' 000877')
,(3, 'start_dt', ' 2018-09-10 00:18:27')
,(4, 'module_position', 'A2_16__1')
,(5, 'head_id HZ0C1', ' 000878')
,(6, 'start_dt', ' 2018-09-10 00:18:27')
SELECT [ErrorCode]
,[ErrorCount]
,SUM(IIF([ErrorCode] = 'module_position', 1, 0)) OVER (ORDER BY [RowID])
FROM #DataSource;
Please, note that you have an certain way to order the rows correctly. You cannot assume that reading them from the table without specifying unique criteria for ordering will always give you the same and the desire result.
In the example above I am using a RowID column. If you have a creation date for example, you can sort by it.
If you do not sort the rows, you can end with head_id of one error to be grouped with another.
You can try the following query.
CREATE TABLE #TempErrorCode
( id int identity(1,1) not null,
[ErrorCode] VARCHAR(50)
,[ErrorCount] VARCHAR(50)
);
INSERT INTO #TempErrorCode ([ErrorCode], [ErrorCount])
VALUES ('module_position', 'A1_16__1')
,('head_id HZ0C1', ' 000877')
,('start_dt', ' 2018-09-10 00:18:27')
,('module_position', 'A2_16__1')
,('head_id HZ0C1', ' 000878')
,('start_dt', ' 2018-09-10 00:18:27')
ALTER TABLE #TempErrorCode ADD RankVal INT
SELECT * INTO #TempRank FROM(
SELECT *, ROW_NUMBER () OVER (PARTITION BY ErrorCode ORDER BY ErrorCode desc) as NewRank FROM #TempErrorCode
)a
--SELECT * FROM #TempRank
UPDATE A
SET RankVal = RA.NewRank
from #TempErrorCode A
INNER JOIN #TempRank RA
ON A.ErrorCode = RA.ErrorCode and A.Id = RA.Id
SELECT * FROM #TempErrorCode order by RankVal
The output is as shown below
id ErrorCode ErrorCount RankVal
1 module_position A1_16__1 1
2 head_id HZ0C1 000877 1
3 start_dt 2018-09-10 00:18:27 1
4 module_position A2_16__1 2
5 head_id HZ0C1 000878 2
6 start_dt 2018-09-10 00:18:27 2
Live Demo <> Rank Update

T-SQL Summation

I'm trying to create result set with 3 columns. Each column coming from the summation of 1 Column of Table A but grouped by different ID's. Here's an overview of what I wanted to do..
Table A
ID Val.1
1 4
1 5
1 6
2 7
2 8
2 9
3 10
3 11
3 12
I wanted to create something like..
ROW SUM.VAL.1 SUM.VAL.2 SUM.VAL.3
1 15 21 33
I understand that I can not get this using UNION, I was thinking of using CTE but not quite sure with the logic.
You need conditional Aggregation
select 1 as Row,
sum(case when ID = 1 then Val.1 end),
sum(case when ID = 2 then Val.1 end),
sum(case when ID = 3 then Val.1 end)
From yourtable
You may need dynamic cross tab or pivot if number of ID's are not static
DECLARE #col_list VARCHAR(8000)= Stuff((SELECT ',sum(case when ID = '+ Cast(ID AS VARCHAR(20))+ ' then [Val.1] end) as [val.'+Cast(ID AS VARCHAR(20))+']'
FROM Yourtable
GROUP BY ID
FOR xml path('')), 1, 1, ''),
#sql VARCHAR(8000)
exec('select 1 as Row,'+#col_list +'from Yourtable')
Live Demo
I think pivoting the data table will yield the desired result.
IF OBJECT_ID('tempdb..#TableA') IS NOT NULL
DROP TABLE #TableA
CREATE TABLE #TableA
(
RowNumber INT,
ID INT,
Value INT
)
INSERT #TableA VALUES (1, 1, 4)
INSERT #TableA VALUES (1, 1, 5)
INSERT #TableA VALUES (1, 1, 6)
INSERT #TableA VALUES (1, 2, 7)
INSERT #TableA VALUES (1, 2, 8)
INSERT #TableA VALUES (1, 2, 9)
INSERT #TableA VALUES (1, 3, 10)
INSERT #TableA VALUES (1, 3, 11)
INSERT #TableA VALUES (1, 3, 12)
-- https://msdn.microsoft.com/en-us/library/ms177410.aspx
SELECT RowNumber, [1] AS Sum1, [2] AS Sum2, [3] AS Sum3
FROM
(
SELECT RowNumber, ID, Value
FROM #TableA
) a
PIVOT
(
SUM(Value)
FOR ID IN ([1], [2], [3])
) AS p
This technique works if the ids you are seeking are constant, otherwise I imagine some dyanmic-sql would work as well if changing ids are needed.
https://msdn.microsoft.com/en-us/library/ms177410.aspx

Segregate data between 2 intervals based on an identifier

One of my friends had this Q to me and I am too puzzled.
His team is loading a DW and the data keeps coming in incremental and full load fashion on an adhoc basic. Now there is identifier flag that says
as to when the full load has started or stopped. Now we need to collect and then segregate all full load.
For ex:
create table #tmp (
id int identity(1,1) not null,
name varchar(30) null,
val int null
)
insert into #tmp (name, val) select 'detroit', 3
insert into #tmp (name, val) select 'california', 9
insert into #tmp (name, val) select 'houston', 1
insert into #tmp (name, val) select 'los angeles', 4
insert into #tmp (name, val) select 'newyork', 8
insert into #tmp (name, val) select 'chicago', 1
insert into #tmp (name, val) select 'seattle', 9
insert into #tmp (name, val) select 'michigan', 6
insert into #tmp (name, val) select 'atlanta', 9
insert into #tmp (name, val) select 'philly', 6
insert into #tmp (name, val) select 'brooklyn', 8
drop table #tmp
The rule is:
whenever val is 9, the full load starts; whenever val is 8, the full
load stops; (or when whenever next val is 8, full load stops).
In this case, for full load, I should only collect these records:
id name val
3 houston 1
4 los angeles 4
10 philly 6
My Approach so far:
;with mycte as (
select id, name, val, row_number() over (order by id) as rnkst
from #tmp
where val in (8,9))
SELECT *
FROM mycte y
WHERE val = 9
AND Exists (
SELECT *
FROM mycte x
WHERE x.id =
----> this gives start 9 record but not stop record of 8
(SELECT MIN(id)
FROM mycte z
WHERE z.id > y.id)
AND val = 8)
I do not want to venture into cursor within cursor approach but with a CTE , please enlighten!
UPDATE:
As mentioned by one of the answerers I am restating the rules.
--> the full load records start coming AFTER 9. (9th records are NOT included)
--> the full load continues till it sees immediate 8.
--> So effectively all records BETWEEN 9 and 8 form small chunks of full load
--> An individual 9th record itself does not get considered as it has no 8 as partner
--> The result set shown below satisfies these conditions
I am not sure if my command of English will allow me to explain my approach fully, but I'll try, just in case it can help.
Rank all the rows and rank the bounds (val IN (8, 9)) separately.
Join the subset where val = 8 with the subset where val = 9 on the condition that the bound ranking of the former should be exactly 1 (one) greater than that of the latter.
Join the subset of non-(8, 9) rows to the result set of Step 2 on the condition that the (general) ranking should be between the ranking of the val = 9 subset and that of the val = 8 one.
Here's the query to illustrate my attempt at verbal description:
WITH ranked AS (
SELECT
*,
rnk = ROW_NUMBER() OVER (ORDER BY id),
bound_rnk = ROW_NUMBER() OVER (
PARTITION BY CASE WHEN val IN (8, 9) THEN 1 ELSE 2 END
ORDER BY id
)
FROM #tmp
)
SELECT
load.id,
load.name,
load.val
FROM ranked AS eight
INNER JOIN ranked AS nine ON eight.bound_rnk = nine.bound_rnk + 1
INNER JOIN ranked AS load ON load.rnk BETWEEN nine.rnk AND eight.rnk
WHERE eight.val = 8
AND nine .val = 9
AND load .val NOT IN (8, 9)
;
And you might not believe me but, when I tested it, it did return the following:
id name val
-- ----------- ---
3 houston 1
4 los angeles 4
10 philly 6
I do not believe there is a way to do this without a while loop or possibly a recursive cte that is going to be complex. So, my question would be if this is at all possible to accomplish in code? SQL is not as strong as a procedural language, so code would handle this better. If this is not an option, then I would go with a while loop (MUCH better than a cursor). I will create the SQL for this shortly.
/*
drop table #tmp
drop table #finalTmp
drop table #startStop
*/
create table #tmp (
id int identity(1,1) not null,
name varchar(30) null,
val int null
)
insert into #tmp (name, val) select 'detroit', 3
insert into #tmp (name, val) select 'california', 9
insert into #tmp (name, val) select 'houston', 1
insert into #tmp (name, val) select 'los angeles', 4
insert into #tmp (name, val) select 'newyork', 8
insert into #tmp (name, val) select 'chicago', 1
insert into #tmp (name, val) select 'seattle', 9
insert into #tmp (name, val) select 'michigan', 6
insert into #tmp (name, val) select 'atlanta', 9
insert into #tmp (name, val) select 'philly', 6
insert into #tmp (name, val) select 'brooklyn', 8
CREATE TABLE #Finaltmp
(
id INT,
name VARCHAR(30),
val INT
)
SELECT id, val, 0 AS Checked
INTO #StartStop
FROM #tmp
WHERE val IN (8,9)
DECLARE #StartId INT, #StopId INT
WHILE EXISTS (SELECT 1 FROM #StartStop WHERE Checked = 0)
BEGIN
SELECT TOP 1 #StopId = id
FROM #StartStop
WHERE EXISTS
--This makes sure we grab a stop that has a start before it
(
SELECT 1
FROM #StartStop AS TestCheck
WHERE TestCheck.id < #StartStop.id AND val = 9
)
AND Checked = 0 AND val = 8
ORDER BY id
--If no more starts, then the rest are stops
IF #StopId IS NULL
BREAK
SELECT TOP 1 #StartId = id
FROM #StartStop
WHERE Checked = 0 AND val = 9
--Make sure we only pick up the 9 that matches
AND Id < #StopId
ORDER BY Id DESC
IF #StartId IS NULL
BREAK
INSERT INTO #Finaltmp
SELECT *
FROM #tmp
WHERE id BETWEEN #StartId AND #StopId
AND val NOT IN (8,9)
--Make sure to "check" any values that fell in the middle (double 9's)
--If not, then you would start picking up overlap data
UPDATE #StartStop
SET Checked = 1
WHERE id <= #StopId
END
SELECT * FROM #Finaltmp
I noticed that the data looked a little wonky, so I tried to put some edge case checks and comments about them

SQL Query for Grouping the results based on sequence

I have a table like this:
ID Seq Amt
1 1 500
1 2 500
1 3 500
1 5 500
2 10 600
2 11 600
3 1 700
3 3 700
I want to group the continuous sequence numbers into a single row like this:
ID Start End TotalAmt
1 1 3 1500
1 5 5 500
2 10 11 1200
3 1 1 700
3 3 3 700
Please help to achieve this result.
WITH numbered AS (
SELECT
ID, Seq, Amt,
SeqGroup = ROW_NUMBER() OVER (PARTITION BY ID ORDER BY Seq) - Seq
FROM atable
)
SELECT
ID,
Start = MIN(Seq),
[End] = MAX(Seq),
TotalAmt = SUM(Amt)
FROM numbered
GROUP BY ID, SeqGroup
ORDER BY ID, Start
;
Well, there's perhaps a more elegant way to do this (something hints at me that there is), but here's an approach that will work if you're using a version of SQL Server that accepts common table expressions:
use Tempdb
go
create table [Test]
(
[id] int not null,
[Seq] int not null,
[Amt] int not null
)
insert into [Test] values
(1, 1, 500),
(1, 2, 500),
(1, 3, 500),
(1, 5, 500),
(2, 10, 600),
(2, 11, 600),
(3, 1, 700),
(3, 3, 700)
;with
lower_bound as (
select *
from Test
where not exists (
select *
from Test as t1
where t1.id = Test.id and t1.Seq = Test.Seq - 1
)
),
upper_bound as (
select *
from Test
where not exists (
select *
from Test as t1
where t1.id = Test.id and t1.Seq = Test.Seq + 1
)
),
bounds as (
select id, (select MAX(seq) from lower_bound where lower_bound.id = upper_bound.id and lower_bound.Seq <= upper_bound.Seq) as LBound, Seq as Ubound
from upper_bound
)
select Test.id, LBound As [Start], UBound As [End], SUM(Amt) As TotalAmt
from Test
join bounds
on Test.id = bounds.id
and Test.Seq between bounds.LBound and bounds.Ubound
group by Test.id, LBound, UBound
drop table [Test]
This seems to work nicely. #breakingRows will contain all rows that break the sequence of id and seq (i.e. if id changes or if seq is not 1 more than the previous seq). With that table you can select all rows of such a sequence within #temp. I must add however that performance will probably be not all that good because of all the subqueries but you'll need to test to be sure.
declare #temp table (id int, seq int, amt int)
insert into #temp select 1, 1, 500
insert into #temp select 1, 2, 500
insert into #temp select 1, 3, 500
insert into #temp select 1, 5, 500
insert into #temp select 2, 10, 600
insert into #temp select 2, 11, 600
insert into #temp select 3, 1, 700
insert into #temp select 3, 3, 700
declare #breakingRows table (ctr int identity(1,1), id int, seq int)
insert into #breakingRows(id, seq)
select id, seq
from #temp t1
where not exists
(select 1 from #temp t2 where t1.id = t2.id and t1.seq - 1 = t2.seq)
order by id, seq
select br.id, br.seq as start,
isnull ((select top 1 seq from #temp t2
where id < (select id from #breakingRows br2 where br.ctr = br2.ctr - 1) or
(id = (select id from #breakingRows br2 where br.ctr = br2.ctr - 1) and
seq < (select seq from #breakingRows br2 where br.ctr = br2.ctr - 1))
order by id desc, seq desc),
br.seq)
as [end],
(select SUM(amt) from #temp t1 where t1.id = br.id and
t1.seq <
isnull((select seq from #breakingRows br2 where br.ctr = br2.ctr - 1 and br.id = br2.id),
(select max(seq) + 1 from #temp)) and
t1.seq >= br.seq)
from #breakingRows br
order by id, seq
Since Andriy has already posted the gold solution, here's my take using an UPDATE statement to get the result from a temp table, just for fun.
declare #tmp table (
id int, seq int, amt money, start int, this int, total money,
primary key clustered(id, seq))
;
insert #tmp
select *, start=seq, this=seq, total=convert(money,amt)
from btable
;
declare #id int, #seq int, #start int, #amt money
update #tmp
set
#amt = total = case when id = #id and seq = #seq+1 then #amt+total else amt end,
#start = start = case when id = #id and seq = #seq+1 then #start else seq end,
#seq = this = seq,
#id = id = id
from #tmp
option (maxdop 1)
;
select id, start, max(this) [end], max(total) total
from #tmp
group by id, start
order by id, start
Notes:
btable: your table name
id int, seq int, amt money: expected columns in your table
Try following query.
select id, min(seq), max(seq), sum(amt) from table group by id
OOps, sorry, it is wrong query as you need sequence
SELECT Id, MIN(Seq) as Start, MAX(Seq) as End, SUM(Amount) as Total
FROM (
SELECT t.*, Seq - ROW_NUMBER() OVER (PARTITION BY Id ORDER BY Seq) Rn
FROM [Table] t
) as T
GROUP BY Id, Rn
ORDER BY Id, MIN(Seq)