How to add an incremental counter to grouped rows, resetting the counter each time a variable changes
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
add a comment |
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
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
add a comment |
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
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
sql postgresql
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
add a comment |
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
add a comment |
1 Answer
1
active
oldest
votes
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
add a comment |
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
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
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
add a comment |
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
add a comment |
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
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
answered Nov 19 '18 at 10:54
FatFreddyFatFreddy
389410
389410
add a comment |
add a comment |
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.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
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