How to add an incremental counter to grouped rows, resetting the counter each time a variable changes










0















I have a PostgreSQL table containing: person_identifier, period_identifier, status



 person | period | status 
-------+--------+--------
Bob | Jan | new
Bob | Feb | retained
Bob | Mar | retained
Bob | Apr | dormant
Bob | May | dormant
Bob | Jun | resurected
Bob | Jul | retained
Bob | Agu | dormant
Jim | Jan | new
Jim | Feb | dormant
Jim | Mar | dormant
Jim | Apr | dormant
Jim | May | dormant
Jim | Jun | resurected
Jim | Jul | dormant
Jim | Agu | resurected


What I need is to include a counter grouping by person, status, with the restriction that the counter needs to restart down to 1 whenever the status changes.



I tried the following query, but this doesn't reset the counter down to 1 whenever a status changes:



SELECT
*,
ROW_NUMBER() OVER (PARTITION BY person, status ORDER BY period) AS wrong_counter
FROM
my_table


Here's the difference of my query and what I actually need; * stands for wrong value:



 person | period | status | wrong_counter | needed_counter
-------+--------+-------------+ --------------+---------------
Bob | Jan | new | 1 | 1
Bob | Feb | retained | 1 | 1
Bob | Mar | retained | 2 | 2
Bob | Apr | dormant | 1 | 1
Bob | May | dormant | 2 | 2
Bob | Jun | resurected | 1 | 1
Bob | Jul | retained | 3* | 1
Bob | Agu | dormant | 3* | 1
Jim | Jan | new | 1 | 1
Jim | Feb | dormant | 1 | 1
Jim | Mar | dormant | 2 | 2
Jim | Apr | dormant | 3 | 3
Jim | May | dormant | 4 | 4
Jim | Jun | resurected | 1 | 1
Jim | Jul | dormant | 5* | 1
Jim | Agu | resurected | 2* | 1


Can anyone help me with this?










share|improve this question
























  • I can't get the logic behind what you want..

    – dwir182
    Nov 15 '18 at 6:25











  • Sample data is best served as DDL + DML. Please edit your question to include it. For more details, read this.

    – Zohar Peled
    Nov 15 '18 at 14:52















0















I have a PostgreSQL table containing: person_identifier, period_identifier, status



 person | period | status 
-------+--------+--------
Bob | Jan | new
Bob | Feb | retained
Bob | Mar | retained
Bob | Apr | dormant
Bob | May | dormant
Bob | Jun | resurected
Bob | Jul | retained
Bob | Agu | dormant
Jim | Jan | new
Jim | Feb | dormant
Jim | Mar | dormant
Jim | Apr | dormant
Jim | May | dormant
Jim | Jun | resurected
Jim | Jul | dormant
Jim | Agu | resurected


What I need is to include a counter grouping by person, status, with the restriction that the counter needs to restart down to 1 whenever the status changes.



I tried the following query, but this doesn't reset the counter down to 1 whenever a status changes:



SELECT
*,
ROW_NUMBER() OVER (PARTITION BY person, status ORDER BY period) AS wrong_counter
FROM
my_table


Here's the difference of my query and what I actually need; * stands for wrong value:



 person | period | status | wrong_counter | needed_counter
-------+--------+-------------+ --------------+---------------
Bob | Jan | new | 1 | 1
Bob | Feb | retained | 1 | 1
Bob | Mar | retained | 2 | 2
Bob | Apr | dormant | 1 | 1
Bob | May | dormant | 2 | 2
Bob | Jun | resurected | 1 | 1
Bob | Jul | retained | 3* | 1
Bob | Agu | dormant | 3* | 1
Jim | Jan | new | 1 | 1
Jim | Feb | dormant | 1 | 1
Jim | Mar | dormant | 2 | 2
Jim | Apr | dormant | 3 | 3
Jim | May | dormant | 4 | 4
Jim | Jun | resurected | 1 | 1
Jim | Jul | dormant | 5* | 1
Jim | Agu | resurected | 2* | 1


Can anyone help me with this?










share|improve this question
























  • I can't get the logic behind what you want..

    – dwir182
    Nov 15 '18 at 6:25











  • Sample data is best served as DDL + DML. Please edit your question to include it. For more details, read this.

    – Zohar Peled
    Nov 15 '18 at 14:52













0












0








0








I have a PostgreSQL table containing: person_identifier, period_identifier, status



 person | period | status 
-------+--------+--------
Bob | Jan | new
Bob | Feb | retained
Bob | Mar | retained
Bob | Apr | dormant
Bob | May | dormant
Bob | Jun | resurected
Bob | Jul | retained
Bob | Agu | dormant
Jim | Jan | new
Jim | Feb | dormant
Jim | Mar | dormant
Jim | Apr | dormant
Jim | May | dormant
Jim | Jun | resurected
Jim | Jul | dormant
Jim | Agu | resurected


What I need is to include a counter grouping by person, status, with the restriction that the counter needs to restart down to 1 whenever the status changes.



I tried the following query, but this doesn't reset the counter down to 1 whenever a status changes:



SELECT
*,
ROW_NUMBER() OVER (PARTITION BY person, status ORDER BY period) AS wrong_counter
FROM
my_table


Here's the difference of my query and what I actually need; * stands for wrong value:



 person | period | status | wrong_counter | needed_counter
-------+--------+-------------+ --------------+---------------
Bob | Jan | new | 1 | 1
Bob | Feb | retained | 1 | 1
Bob | Mar | retained | 2 | 2
Bob | Apr | dormant | 1 | 1
Bob | May | dormant | 2 | 2
Bob | Jun | resurected | 1 | 1
Bob | Jul | retained | 3* | 1
Bob | Agu | dormant | 3* | 1
Jim | Jan | new | 1 | 1
Jim | Feb | dormant | 1 | 1
Jim | Mar | dormant | 2 | 2
Jim | Apr | dormant | 3 | 3
Jim | May | dormant | 4 | 4
Jim | Jun | resurected | 1 | 1
Jim | Jul | dormant | 5* | 1
Jim | Agu | resurected | 2* | 1


Can anyone help me with this?










share|improve this question
















I have a PostgreSQL table containing: person_identifier, period_identifier, status



 person | period | status 
-------+--------+--------
Bob | Jan | new
Bob | Feb | retained
Bob | Mar | retained
Bob | Apr | dormant
Bob | May | dormant
Bob | Jun | resurected
Bob | Jul | retained
Bob | Agu | dormant
Jim | Jan | new
Jim | Feb | dormant
Jim | Mar | dormant
Jim | Apr | dormant
Jim | May | dormant
Jim | Jun | resurected
Jim | Jul | dormant
Jim | Agu | resurected


What I need is to include a counter grouping by person, status, with the restriction that the counter needs to restart down to 1 whenever the status changes.



I tried the following query, but this doesn't reset the counter down to 1 whenever a status changes:



SELECT
*,
ROW_NUMBER() OVER (PARTITION BY person, status ORDER BY period) AS wrong_counter
FROM
my_table


Here's the difference of my query and what I actually need; * stands for wrong value:



 person | period | status | wrong_counter | needed_counter
-------+--------+-------------+ --------------+---------------
Bob | Jan | new | 1 | 1
Bob | Feb | retained | 1 | 1
Bob | Mar | retained | 2 | 2
Bob | Apr | dormant | 1 | 1
Bob | May | dormant | 2 | 2
Bob | Jun | resurected | 1 | 1
Bob | Jul | retained | 3* | 1
Bob | Agu | dormant | 3* | 1
Jim | Jan | new | 1 | 1
Jim | Feb | dormant | 1 | 1
Jim | Mar | dormant | 2 | 2
Jim | Apr | dormant | 3 | 3
Jim | May | dormant | 4 | 4
Jim | Jun | resurected | 1 | 1
Jim | Jul | dormant | 5* | 1
Jim | Agu | resurected | 2* | 1


Can anyone help me with this?







sql postgresql






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 15 '18 at 16:29







Diego Tangassi

















asked Nov 15 '18 at 0:15









Diego TangassiDiego Tangassi

32




32












  • I can't get the logic behind what you want..

    – dwir182
    Nov 15 '18 at 6:25











  • Sample data is best served as DDL + DML. Please edit your question to include it. For more details, read this.

    – Zohar Peled
    Nov 15 '18 at 14:52

















  • I can't get the logic behind what you want..

    – dwir182
    Nov 15 '18 at 6:25











  • Sample data is best served as DDL + DML. Please edit your question to include it. For more details, read this.

    – Zohar Peled
    Nov 15 '18 at 14:52
















I can't get the logic behind what you want..

– dwir182
Nov 15 '18 at 6:25





I can't get the logic behind what you want..

– dwir182
Nov 15 '18 at 6:25













Sample data is best served as DDL + DML. Please edit your question to include it. For more details, read this.

– Zohar Peled
Nov 15 '18 at 14:52





Sample data is best served as DDL + DML. Please edit your question to include it. For more details, read this.

– Zohar Peled
Nov 15 '18 at 14:52












1 Answer
1






active

oldest

votes


















0














i did some normalisation :



person -> person_fk,
period -> period_int, Jan = 1 ...
status -> status_fk


and use as basetable : public.tbl_test



then i do some calculations: first get the status in the line before the actual line and is it a status_change or not.
make a help_partition and then get the row_number over the help_partition.



with temp_base_data as 
(
select
*,
lag(status_fk,1,-1) over(ORDER BY person_fk, period_int) as status_before,
case
when lag(status_fk,1,-1) over(ORDER BY person_fk, period_int) = status_fk and lag(person_fk ,1,-1) over(ORDER BY person_fk, period_int) = person_fk
then 0
else 1
end as status_change
from public.tbl_test
order by person_fk, period_int
),
temp_partition AS
(
select
*,
sum(status_change) over ( order by person_fk, period_int RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) as help_partition
from temp_base_data
order by person_fk, period_int
)

select
* ,
row_number() over (PARTITION by help_partition order by person_fk, period_int) as counter
from temp_partition
order by id


and result in : (last row is the counter you need)



person period status id period_int person_fk status_fk status_before status_change help_partition counter
Bob Jan new 1 1 1 1 -1 1 1 1
Bob Feb retained 2 2 1 2 1 1 2 1
Bob Mar retained 3 3 1 2 2 0 2 2
Bob Apr dormant 4 4 1 3 2 1 3 1
Bob May dormant 5 5 1 3 3 0 3 2
Bob Jun resurected 6 6 1 4 3 1 4 1
Bob Jul retained 7 7 1 2 4 1 5 1
Bob Agu dormant 8 8 1 3 2 1 6 1
Jim Jan new 9 1 2 1 3 1 7 1
Jim Feb dormant 10 2 2 3 1 1 8 1
Jim Mar dormant 11 3 2 3 3 0 8 2
Jim Apr dormant 12 4 2 3 3 0 8 3
Jim May dormant 13 5 2 3 3 0 8 4
Jim Jun resurected 14 6 2 4 3 1 9 1
Jim Jul dormant 15 7 2 3 4 1 10 1
Jim Agu resurected 16 8 2 4 3 1 11 1





share|improve this answer






















    Your Answer






    StackExchange.ifUsing("editor", function ()
    StackExchange.using("externalEditor", function ()
    StackExchange.using("snippets", function ()
    StackExchange.snippets.init();
    );
    );
    , "code-snippets");

    StackExchange.ready(function()
    var channelOptions =
    tags: "".split(" "),
    id: "1"
    ;
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function()
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled)
    StackExchange.using("snippets", function()
    createEditor();
    );

    else
    createEditor();

    );

    function createEditor()
    StackExchange.prepareEditor(
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader:
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    ,
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    );



    );













    draft saved

    draft discarded


















    StackExchange.ready(
    function ()
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53310689%2fhow-to-add-an-incremental-counter-to-grouped-rows-resetting-the-counter-each-ti%23new-answer', 'question_page');

    );

    Post as a guest















    Required, but never shown

























    1 Answer
    1






    active

    oldest

    votes








    1 Answer
    1






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    0














    i did some normalisation :



    person -> person_fk,
    period -> period_int, Jan = 1 ...
    status -> status_fk


    and use as basetable : public.tbl_test



    then i do some calculations: first get the status in the line before the actual line and is it a status_change or not.
    make a help_partition and then get the row_number over the help_partition.



    with temp_base_data as 
    (
    select
    *,
    lag(status_fk,1,-1) over(ORDER BY person_fk, period_int) as status_before,
    case
    when lag(status_fk,1,-1) over(ORDER BY person_fk, period_int) = status_fk and lag(person_fk ,1,-1) over(ORDER BY person_fk, period_int) = person_fk
    then 0
    else 1
    end as status_change
    from public.tbl_test
    order by person_fk, period_int
    ),
    temp_partition AS
    (
    select
    *,
    sum(status_change) over ( order by person_fk, period_int RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) as help_partition
    from temp_base_data
    order by person_fk, period_int
    )

    select
    * ,
    row_number() over (PARTITION by help_partition order by person_fk, period_int) as counter
    from temp_partition
    order by id


    and result in : (last row is the counter you need)



    person period status id period_int person_fk status_fk status_before status_change help_partition counter
    Bob Jan new 1 1 1 1 -1 1 1 1
    Bob Feb retained 2 2 1 2 1 1 2 1
    Bob Mar retained 3 3 1 2 2 0 2 2
    Bob Apr dormant 4 4 1 3 2 1 3 1
    Bob May dormant 5 5 1 3 3 0 3 2
    Bob Jun resurected 6 6 1 4 3 1 4 1
    Bob Jul retained 7 7 1 2 4 1 5 1
    Bob Agu dormant 8 8 1 3 2 1 6 1
    Jim Jan new 9 1 2 1 3 1 7 1
    Jim Feb dormant 10 2 2 3 1 1 8 1
    Jim Mar dormant 11 3 2 3 3 0 8 2
    Jim Apr dormant 12 4 2 3 3 0 8 3
    Jim May dormant 13 5 2 3 3 0 8 4
    Jim Jun resurected 14 6 2 4 3 1 9 1
    Jim Jul dormant 15 7 2 3 4 1 10 1
    Jim Agu resurected 16 8 2 4 3 1 11 1





    share|improve this answer



























      0














      i did some normalisation :



      person -> person_fk,
      period -> period_int, Jan = 1 ...
      status -> status_fk


      and use as basetable : public.tbl_test



      then i do some calculations: first get the status in the line before the actual line and is it a status_change or not.
      make a help_partition and then get the row_number over the help_partition.



      with temp_base_data as 
      (
      select
      *,
      lag(status_fk,1,-1) over(ORDER BY person_fk, period_int) as status_before,
      case
      when lag(status_fk,1,-1) over(ORDER BY person_fk, period_int) = status_fk and lag(person_fk ,1,-1) over(ORDER BY person_fk, period_int) = person_fk
      then 0
      else 1
      end as status_change
      from public.tbl_test
      order by person_fk, period_int
      ),
      temp_partition AS
      (
      select
      *,
      sum(status_change) over ( order by person_fk, period_int RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) as help_partition
      from temp_base_data
      order by person_fk, period_int
      )

      select
      * ,
      row_number() over (PARTITION by help_partition order by person_fk, period_int) as counter
      from temp_partition
      order by id


      and result in : (last row is the counter you need)



      person period status id period_int person_fk status_fk status_before status_change help_partition counter
      Bob Jan new 1 1 1 1 -1 1 1 1
      Bob Feb retained 2 2 1 2 1 1 2 1
      Bob Mar retained 3 3 1 2 2 0 2 2
      Bob Apr dormant 4 4 1 3 2 1 3 1
      Bob May dormant 5 5 1 3 3 0 3 2
      Bob Jun resurected 6 6 1 4 3 1 4 1
      Bob Jul retained 7 7 1 2 4 1 5 1
      Bob Agu dormant 8 8 1 3 2 1 6 1
      Jim Jan new 9 1 2 1 3 1 7 1
      Jim Feb dormant 10 2 2 3 1 1 8 1
      Jim Mar dormant 11 3 2 3 3 0 8 2
      Jim Apr dormant 12 4 2 3 3 0 8 3
      Jim May dormant 13 5 2 3 3 0 8 4
      Jim Jun resurected 14 6 2 4 3 1 9 1
      Jim Jul dormant 15 7 2 3 4 1 10 1
      Jim Agu resurected 16 8 2 4 3 1 11 1





      share|improve this answer

























        0












        0








        0







        i did some normalisation :



        person -> person_fk,
        period -> period_int, Jan = 1 ...
        status -> status_fk


        and use as basetable : public.tbl_test



        then i do some calculations: first get the status in the line before the actual line and is it a status_change or not.
        make a help_partition and then get the row_number over the help_partition.



        with temp_base_data as 
        (
        select
        *,
        lag(status_fk,1,-1) over(ORDER BY person_fk, period_int) as status_before,
        case
        when lag(status_fk,1,-1) over(ORDER BY person_fk, period_int) = status_fk and lag(person_fk ,1,-1) over(ORDER BY person_fk, period_int) = person_fk
        then 0
        else 1
        end as status_change
        from public.tbl_test
        order by person_fk, period_int
        ),
        temp_partition AS
        (
        select
        *,
        sum(status_change) over ( order by person_fk, period_int RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) as help_partition
        from temp_base_data
        order by person_fk, period_int
        )

        select
        * ,
        row_number() over (PARTITION by help_partition order by person_fk, period_int) as counter
        from temp_partition
        order by id


        and result in : (last row is the counter you need)



        person period status id period_int person_fk status_fk status_before status_change help_partition counter
        Bob Jan new 1 1 1 1 -1 1 1 1
        Bob Feb retained 2 2 1 2 1 1 2 1
        Bob Mar retained 3 3 1 2 2 0 2 2
        Bob Apr dormant 4 4 1 3 2 1 3 1
        Bob May dormant 5 5 1 3 3 0 3 2
        Bob Jun resurected 6 6 1 4 3 1 4 1
        Bob Jul retained 7 7 1 2 4 1 5 1
        Bob Agu dormant 8 8 1 3 2 1 6 1
        Jim Jan new 9 1 2 1 3 1 7 1
        Jim Feb dormant 10 2 2 3 1 1 8 1
        Jim Mar dormant 11 3 2 3 3 0 8 2
        Jim Apr dormant 12 4 2 3 3 0 8 3
        Jim May dormant 13 5 2 3 3 0 8 4
        Jim Jun resurected 14 6 2 4 3 1 9 1
        Jim Jul dormant 15 7 2 3 4 1 10 1
        Jim Agu resurected 16 8 2 4 3 1 11 1





        share|improve this answer













        i did some normalisation :



        person -> person_fk,
        period -> period_int, Jan = 1 ...
        status -> status_fk


        and use as basetable : public.tbl_test



        then i do some calculations: first get the status in the line before the actual line and is it a status_change or not.
        make a help_partition and then get the row_number over the help_partition.



        with temp_base_data as 
        (
        select
        *,
        lag(status_fk,1,-1) over(ORDER BY person_fk, period_int) as status_before,
        case
        when lag(status_fk,1,-1) over(ORDER BY person_fk, period_int) = status_fk and lag(person_fk ,1,-1) over(ORDER BY person_fk, period_int) = person_fk
        then 0
        else 1
        end as status_change
        from public.tbl_test
        order by person_fk, period_int
        ),
        temp_partition AS
        (
        select
        *,
        sum(status_change) over ( order by person_fk, period_int RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) as help_partition
        from temp_base_data
        order by person_fk, period_int
        )

        select
        * ,
        row_number() over (PARTITION by help_partition order by person_fk, period_int) as counter
        from temp_partition
        order by id


        and result in : (last row is the counter you need)



        person period status id period_int person_fk status_fk status_before status_change help_partition counter
        Bob Jan new 1 1 1 1 -1 1 1 1
        Bob Feb retained 2 2 1 2 1 1 2 1
        Bob Mar retained 3 3 1 2 2 0 2 2
        Bob Apr dormant 4 4 1 3 2 1 3 1
        Bob May dormant 5 5 1 3 3 0 3 2
        Bob Jun resurected 6 6 1 4 3 1 4 1
        Bob Jul retained 7 7 1 2 4 1 5 1
        Bob Agu dormant 8 8 1 3 2 1 6 1
        Jim Jan new 9 1 2 1 3 1 7 1
        Jim Feb dormant 10 2 2 3 1 1 8 1
        Jim Mar dormant 11 3 2 3 3 0 8 2
        Jim Apr dormant 12 4 2 3 3 0 8 3
        Jim May dormant 13 5 2 3 3 0 8 4
        Jim Jun resurected 14 6 2 4 3 1 9 1
        Jim Jul dormant 15 7 2 3 4 1 10 1
        Jim Agu resurected 16 8 2 4 3 1 11 1






        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Nov 19 '18 at 10:54









        FatFreddyFatFreddy

        389410




        389410





























            draft saved

            draft discarded
















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid


            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.

            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function ()
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53310689%2fhow-to-add-an-incremental-counter-to-grouped-rows-resetting-the-counter-each-ti%23new-answer', 'question_page');

            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            Use pre created SQLite database for Android project in kotlin

            Darth Vader #20

            Ondo