HiveBrain v1.2.0
Get Started
← Back to all entries
patternsqlMinor

Assign number incrementally, reset when value changes

Submitted by: @import:stackexchange-dba··
0
Viewed 0 times
incrementallynumbervaluechangeswhenresetassign

Problem

declare @t table (scanCode varchar(6), dates datetime, flag varchar(1))

insert @t ( scanCode, dates, flag)
select '182086','01 Jul 2020','P' union all
select'182086','02 Jul 2020','P' union all
select'182086','03 Jul 2020','A' union all
select'182086','04 Jul 2020','A' union all
select'182086','06 Jul 2020','P' union all
select'182086','07 Jul 2020','P' union all
select'182086','08 Jul 2020','P' union all
select'182086','09 Jul 2020','P' union all
select'182086','10 Jul 2020','A' union all
select'182086','11 Jul 2020','A' union all
select'182086','13 Jul 2020','A' union all
select'182086','14 Jul 2020','A'

select scanCode
    , dates
    , flag
    , prn = row_number() over (partition by scanCode,flag order by scanCode, dates) 
from @t t
order by t.dates


Currently the result is:

scanCode    dates   flag    prn
182086      2020/07/01  P   1
182086      2020/07/02  P   2
182086      2020/07/03  A   1
182086      2020/07/04  A   2
182086      2020/07/06  P   3
182086      2020/07/07  P   4
182086      2020/07/08  P   5
182086      2020/07/09  P   6
182086      2020/07/10  A   3
182086      2020/07/11  A   4
182086      2020/07/13  A   5
182086      2020/07/14  A   6


I want the result to be:

scanCode    dates   flag    prn
182086      2020/07/01  P   1
182086      2020/07/02  P   2
182086      2020/07/03  A   1
182086      2020/07/04  A   2
182086      2020/07/06  P   1
182086      2020/07/07  P   2
182086      2020/07/08  P   3
182086      2020/07/09  P   4
182086      2020/07/10  A   1
182086      2020/07/11  A   2
182086      2020/07/13  A   3
182086      2020/07/14  A   4


scancode, dates is primary key in my table.

Solution

Another way to solve this is to think of it as a variation of the gaps and islands pattern:

SELECT *, diff = (N.rn1 - N.rn2)
FROM 
(
    SELECT 
        *, 
        rn1 = ROW_NUMBER() OVER (
            PARTITION BY T.scanCode 
            ORDER BY T.dates),
        rn2 = ROW_NUMBER() OVER (
            PARTITION BY T.scanCode 
            ORDER BY T.flag, T.dates)
    FROM @t AS T
) AS N;


That output gives us a way to identify groups:

All that is left to do is number rows within each group. The full query is:

WITH
    N AS 
    (
        -- Previous code
        SELECT 
            *, 
            rn1 = ROW_NUMBER() OVER (
                PARTITION BY T.scanCode 
                ORDER BY T.dates),
            rn2 = ROW_NUMBER() OVER (
                PARTITION BY T.scanCode 
                ORDER BY T.flag, T.dates)
        FROM @t AS T
    )
SELECT
    N.scanCode,
    N.dates,
    N.flag,
    prn = ROW_NUMBER() OVER (
        PARTITION BY N.scanCode, (N.rn1 - N.rn2)
        ORDER BY N.dates)
FROM N;


The output is:

Execution plan:

db<>fiddle online demo

Code Snippets

SELECT *, diff = (N.rn1 - N.rn2)
FROM 
(
    SELECT 
        *, 
        rn1 = ROW_NUMBER() OVER (
            PARTITION BY T.scanCode 
            ORDER BY T.dates),
        rn2 = ROW_NUMBER() OVER (
            PARTITION BY T.scanCode 
            ORDER BY T.flag, T.dates)
    FROM @t AS T
) AS N;
WITH
    N AS 
    (
        -- Previous code
        SELECT 
            *, 
            rn1 = ROW_NUMBER() OVER (
                PARTITION BY T.scanCode 
                ORDER BY T.dates),
            rn2 = ROW_NUMBER() OVER (
                PARTITION BY T.scanCode 
                ORDER BY T.flag, T.dates)
        FROM @t AS T
    )
SELECT
    N.scanCode,
    N.dates,
    N.flag,
    prn = ROW_NUMBER() OVER (
        PARTITION BY N.scanCode, (N.rn1 - N.rn2)
        ORDER BY N.dates)
FROM N;

Context

StackExchange Database Administrators Q#274679, answer score: 3

Revisions (0)

No revisions yet.