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











up vote
0
down vote

favorite












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 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 at 14:52















up vote
0
down vote

favorite












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 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 at 14:52













up vote
0
down vote

favorite









up vote
0
down vote

favorite











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 at 16:29

























asked Nov 15 at 0:15









Diego Tangassi

32




32












  • I can't get the logic behind what you want..
    – dwir182
    Nov 15 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 at 14:52


















  • I can't get the logic behind what you want..
    – dwir182
    Nov 15 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 at 14:52
















I can't get the logic behind what you want..
– dwir182
Nov 15 at 6:25




I can't get the logic behind what you want..
– dwir182
Nov 15 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 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 at 14:52












1 Answer
1






active

oldest

votes

















up vote
0
down vote



accepted










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',
    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








    up vote
    0
    down vote



    accepted










    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

























      up vote
      0
      down vote



      accepted










      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























        up vote
        0
        down vote



        accepted







        up vote
        0
        down vote



        accepted






        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 at 10:54









        FatFreddy

        35419




        35419






























            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.





            Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


            Please pay close attention to the following guidance:


            • 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

            Biblatex bibliography style without URLs when DOI exists (in Overleaf with Zotero bibliography)

            ComboBox Display Member on multiple fields

            Is it possible to collect Nectar points via Trainline?