Find all items where the 2 latest statuses meet condition
I have a table in a PostgreSQL database like this:
ID|Item-FK |timestamp|status
=============================
1 | 123 | ... | EXPIRED
2 | 123 | ... | PENDING
...
I want to query all items where the last two statuses are 'EXPIRED' & 'PENDING' like displayed in the sample above - 'PENDING' latest and 'EXPIRED' immediately before that.
postgresql postgresql-9.4 greatest-n-per-group
add a comment |
I have a table in a PostgreSQL database like this:
ID|Item-FK |timestamp|status
=============================
1 | 123 | ... | EXPIRED
2 | 123 | ... | PENDING
...
I want to query all items where the last two statuses are 'EXPIRED' & 'PENDING' like displayed in the sample above - 'PENDING' latest and 'EXPIRED' immediately before that.
postgresql postgresql-9.4 greatest-n-per-group
1
Is "last" defined bytimestamp
orID
? Is there also a tableitems
with with 1 row per item of interest (like "FK" seems to suggest)? And how many rows per item on average?
– Erwin Brandstetter
Dec 24 '18 at 3:42
add a comment |
I have a table in a PostgreSQL database like this:
ID|Item-FK |timestamp|status
=============================
1 | 123 | ... | EXPIRED
2 | 123 | ... | PENDING
...
I want to query all items where the last two statuses are 'EXPIRED' & 'PENDING' like displayed in the sample above - 'PENDING' latest and 'EXPIRED' immediately before that.
postgresql postgresql-9.4 greatest-n-per-group
I have a table in a PostgreSQL database like this:
ID|Item-FK |timestamp|status
=============================
1 | 123 | ... | EXPIRED
2 | 123 | ... | PENDING
...
I want to query all items where the last two statuses are 'EXPIRED' & 'PENDING' like displayed in the sample above - 'PENDING' latest and 'EXPIRED' immediately before that.
postgresql postgresql-9.4 greatest-n-per-group
postgresql postgresql-9.4 greatest-n-per-group
edited Dec 24 '18 at 4:24
Erwin Brandstetter
95.2k9185300
95.2k9185300
asked Dec 22 '18 at 23:40
BarcelonaBarcelona
1133
1133
1
Is "last" defined bytimestamp
orID
? Is there also a tableitems
with with 1 row per item of interest (like "FK" seems to suggest)? And how many rows per item on average?
– Erwin Brandstetter
Dec 24 '18 at 3:42
add a comment |
1
Is "last" defined bytimestamp
orID
? Is there also a tableitems
with with 1 row per item of interest (like "FK" seems to suggest)? And how many rows per item on average?
– Erwin Brandstetter
Dec 24 '18 at 3:42
1
1
Is "last" defined by
timestamp
or ID
? Is there also a table items
with with 1 row per item of interest (like "FK" seems to suggest)? And how many rows per item on average?– Erwin Brandstetter
Dec 24 '18 at 3:42
Is "last" defined by
timestamp
or ID
? Is there also a table items
with with 1 row per item of interest (like "FK" seems to suggest)? And how many rows per item on average?– Erwin Brandstetter
Dec 24 '18 at 3:42
add a comment |
3 Answers
3
active
oldest
votes
You could do it like this to avoid needing to hardcode row numbers into your query:
select *
from t as a
where status = 'pending' -- must be pending
and exists
(select *
from t as b
where status = 'expired' -- must have expired
and a.itemfk = b.itemfk
and b.id =
(select max(c.id) -- must be the previous entry before pending
from t as c
where c.itemfk = b.itemfk
and c.id < a.id) -- must have expired before pending
);
This method performs a lookup to get the maximum id
of the entry prior to it being pending and then checks that it is the row marked as expired
.
db<>fiddle example.
2
I don't see how does this check that the latest status is 'pending'.
– ypercubeᵀᴹ
Dec 24 '18 at 5:26
add a comment |
CTE add a row number in descending order, that means that row (1) must be PENDING and row (2) must be EXPIRED.
Then you can use 2 EXISTS subqueries in the WHERE clause to ensure both conditions are true.
with ct as
(
select
id, itemfk, dt, status,
row_number() over (partition by itemfk order by itemfk, dt desc) rn
from
t
order by
itemfk, dt
)
select
t1.itemfk
from
ct t1
where exists (select 1
from ct
where itemfk = t1.itemfk
and rn = 1 and status = 'pending')
and
exists (select 1
from ct
where itemfk = t1.itemfk
and rn = 2 and status = 'expired')
group by t1.itemfk;
db<>fiddle here
Could u give me a generic solution? I use Hibernate in java and need a simpler query to execute
– Barcelona
Dec 23 '18 at 0:34
add a comment |
Assuming ..
- an
item
table with exactly 1 row per item of interest (as indicated by the column nameitem_fk
(="Item-FK"
). - that "last" is defined by the column
ts
(=timestamp
) - which isNOT NULL
. (Might alternatively beid
.) - that
(item_fk, ts)
is definedUNIQUE
(else you need moreORDER BY
expressions to make it deterministic)
Then this should be fastest and simplest:
SELECT i.* -- or what you need from item
FROM item i
JOIN LATERAL (
SELECT ARRAY (
SELECT status
FROM tbl t
WHERE t.itemfk = i.item_id
ORDER BY ts DESC
LIMIT 2
) AS last2_stat
) t ON t.last2_stat = '{PENDING, EXPIRED}'::text;
db<>fiddle here
You can return columns from table item
directly.
It is simple to adapt to any other terminal sequence of statuses.
Have this index for best performance:
CREATE INDEX ON tbl (itemfk, id DESC, status);
status
as last index expression only if you get index-only scans out of it.
Related, with more explanation:
- Optimize GROUP BY query to retrieve latest record per user
- Select first row in each GROUP BY group?
- What is the difference between LATERAL and a subquery in PostgreSQL?
- Why is array_agg() slower than the non-aggregate ARRAY() constructor?
add a comment |
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "182"
};
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: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
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%2fdba.stackexchange.com%2fquestions%2f225650%2ffind-all-items-where-the-2-latest-statuses-meet-condition%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
You could do it like this to avoid needing to hardcode row numbers into your query:
select *
from t as a
where status = 'pending' -- must be pending
and exists
(select *
from t as b
where status = 'expired' -- must have expired
and a.itemfk = b.itemfk
and b.id =
(select max(c.id) -- must be the previous entry before pending
from t as c
where c.itemfk = b.itemfk
and c.id < a.id) -- must have expired before pending
);
This method performs a lookup to get the maximum id
of the entry prior to it being pending and then checks that it is the row marked as expired
.
db<>fiddle example.
2
I don't see how does this check that the latest status is 'pending'.
– ypercubeᵀᴹ
Dec 24 '18 at 5:26
add a comment |
You could do it like this to avoid needing to hardcode row numbers into your query:
select *
from t as a
where status = 'pending' -- must be pending
and exists
(select *
from t as b
where status = 'expired' -- must have expired
and a.itemfk = b.itemfk
and b.id =
(select max(c.id) -- must be the previous entry before pending
from t as c
where c.itemfk = b.itemfk
and c.id < a.id) -- must have expired before pending
);
This method performs a lookup to get the maximum id
of the entry prior to it being pending and then checks that it is the row marked as expired
.
db<>fiddle example.
2
I don't see how does this check that the latest status is 'pending'.
– ypercubeᵀᴹ
Dec 24 '18 at 5:26
add a comment |
You could do it like this to avoid needing to hardcode row numbers into your query:
select *
from t as a
where status = 'pending' -- must be pending
and exists
(select *
from t as b
where status = 'expired' -- must have expired
and a.itemfk = b.itemfk
and b.id =
(select max(c.id) -- must be the previous entry before pending
from t as c
where c.itemfk = b.itemfk
and c.id < a.id) -- must have expired before pending
);
This method performs a lookup to get the maximum id
of the entry prior to it being pending and then checks that it is the row marked as expired
.
db<>fiddle example.
You could do it like this to avoid needing to hardcode row numbers into your query:
select *
from t as a
where status = 'pending' -- must be pending
and exists
(select *
from t as b
where status = 'expired' -- must have expired
and a.itemfk = b.itemfk
and b.id =
(select max(c.id) -- must be the previous entry before pending
from t as c
where c.itemfk = b.itemfk
and c.id < a.id) -- must have expired before pending
);
This method performs a lookup to get the maximum id
of the entry prior to it being pending and then checks that it is the row marked as expired
.
db<>fiddle example.
answered Dec 23 '18 at 0:44
Mr.BrownstoneMr.Brownstone
9,75232342
9,75232342
2
I don't see how does this check that the latest status is 'pending'.
– ypercubeᵀᴹ
Dec 24 '18 at 5:26
add a comment |
2
I don't see how does this check that the latest status is 'pending'.
– ypercubeᵀᴹ
Dec 24 '18 at 5:26
2
2
I don't see how does this check that the latest status is 'pending'.
– ypercubeᵀᴹ
Dec 24 '18 at 5:26
I don't see how does this check that the latest status is 'pending'.
– ypercubeᵀᴹ
Dec 24 '18 at 5:26
add a comment |
CTE add a row number in descending order, that means that row (1) must be PENDING and row (2) must be EXPIRED.
Then you can use 2 EXISTS subqueries in the WHERE clause to ensure both conditions are true.
with ct as
(
select
id, itemfk, dt, status,
row_number() over (partition by itemfk order by itemfk, dt desc) rn
from
t
order by
itemfk, dt
)
select
t1.itemfk
from
ct t1
where exists (select 1
from ct
where itemfk = t1.itemfk
and rn = 1 and status = 'pending')
and
exists (select 1
from ct
where itemfk = t1.itemfk
and rn = 2 and status = 'expired')
group by t1.itemfk;
db<>fiddle here
Could u give me a generic solution? I use Hibernate in java and need a simpler query to execute
– Barcelona
Dec 23 '18 at 0:34
add a comment |
CTE add a row number in descending order, that means that row (1) must be PENDING and row (2) must be EXPIRED.
Then you can use 2 EXISTS subqueries in the WHERE clause to ensure both conditions are true.
with ct as
(
select
id, itemfk, dt, status,
row_number() over (partition by itemfk order by itemfk, dt desc) rn
from
t
order by
itemfk, dt
)
select
t1.itemfk
from
ct t1
where exists (select 1
from ct
where itemfk = t1.itemfk
and rn = 1 and status = 'pending')
and
exists (select 1
from ct
where itemfk = t1.itemfk
and rn = 2 and status = 'expired')
group by t1.itemfk;
db<>fiddle here
Could u give me a generic solution? I use Hibernate in java and need a simpler query to execute
– Barcelona
Dec 23 '18 at 0:34
add a comment |
CTE add a row number in descending order, that means that row (1) must be PENDING and row (2) must be EXPIRED.
Then you can use 2 EXISTS subqueries in the WHERE clause to ensure both conditions are true.
with ct as
(
select
id, itemfk, dt, status,
row_number() over (partition by itemfk order by itemfk, dt desc) rn
from
t
order by
itemfk, dt
)
select
t1.itemfk
from
ct t1
where exists (select 1
from ct
where itemfk = t1.itemfk
and rn = 1 and status = 'pending')
and
exists (select 1
from ct
where itemfk = t1.itemfk
and rn = 2 and status = 'expired')
group by t1.itemfk;
db<>fiddle here
CTE add a row number in descending order, that means that row (1) must be PENDING and row (2) must be EXPIRED.
Then you can use 2 EXISTS subqueries in the WHERE clause to ensure both conditions are true.
with ct as
(
select
id, itemfk, dt, status,
row_number() over (partition by itemfk order by itemfk, dt desc) rn
from
t
order by
itemfk, dt
)
select
t1.itemfk
from
ct t1
where exists (select 1
from ct
where itemfk = t1.itemfk
and rn = 1 and status = 'pending')
and
exists (select 1
from ct
where itemfk = t1.itemfk
and rn = 2 and status = 'expired')
group by t1.itemfk;
db<>fiddle here
answered Dec 23 '18 at 0:00
McNetsMcNets
16.3k42161
16.3k42161
Could u give me a generic solution? I use Hibernate in java and need a simpler query to execute
– Barcelona
Dec 23 '18 at 0:34
add a comment |
Could u give me a generic solution? I use Hibernate in java and need a simpler query to execute
– Barcelona
Dec 23 '18 at 0:34
Could u give me a generic solution? I use Hibernate in java and need a simpler query to execute
– Barcelona
Dec 23 '18 at 0:34
Could u give me a generic solution? I use Hibernate in java and need a simpler query to execute
– Barcelona
Dec 23 '18 at 0:34
add a comment |
Assuming ..
- an
item
table with exactly 1 row per item of interest (as indicated by the column nameitem_fk
(="Item-FK"
). - that "last" is defined by the column
ts
(=timestamp
) - which isNOT NULL
. (Might alternatively beid
.) - that
(item_fk, ts)
is definedUNIQUE
(else you need moreORDER BY
expressions to make it deterministic)
Then this should be fastest and simplest:
SELECT i.* -- or what you need from item
FROM item i
JOIN LATERAL (
SELECT ARRAY (
SELECT status
FROM tbl t
WHERE t.itemfk = i.item_id
ORDER BY ts DESC
LIMIT 2
) AS last2_stat
) t ON t.last2_stat = '{PENDING, EXPIRED}'::text;
db<>fiddle here
You can return columns from table item
directly.
It is simple to adapt to any other terminal sequence of statuses.
Have this index for best performance:
CREATE INDEX ON tbl (itemfk, id DESC, status);
status
as last index expression only if you get index-only scans out of it.
Related, with more explanation:
- Optimize GROUP BY query to retrieve latest record per user
- Select first row in each GROUP BY group?
- What is the difference between LATERAL and a subquery in PostgreSQL?
- Why is array_agg() slower than the non-aggregate ARRAY() constructor?
add a comment |
Assuming ..
- an
item
table with exactly 1 row per item of interest (as indicated by the column nameitem_fk
(="Item-FK"
). - that "last" is defined by the column
ts
(=timestamp
) - which isNOT NULL
. (Might alternatively beid
.) - that
(item_fk, ts)
is definedUNIQUE
(else you need moreORDER BY
expressions to make it deterministic)
Then this should be fastest and simplest:
SELECT i.* -- or what you need from item
FROM item i
JOIN LATERAL (
SELECT ARRAY (
SELECT status
FROM tbl t
WHERE t.itemfk = i.item_id
ORDER BY ts DESC
LIMIT 2
) AS last2_stat
) t ON t.last2_stat = '{PENDING, EXPIRED}'::text;
db<>fiddle here
You can return columns from table item
directly.
It is simple to adapt to any other terminal sequence of statuses.
Have this index for best performance:
CREATE INDEX ON tbl (itemfk, id DESC, status);
status
as last index expression only if you get index-only scans out of it.
Related, with more explanation:
- Optimize GROUP BY query to retrieve latest record per user
- Select first row in each GROUP BY group?
- What is the difference between LATERAL and a subquery in PostgreSQL?
- Why is array_agg() slower than the non-aggregate ARRAY() constructor?
add a comment |
Assuming ..
- an
item
table with exactly 1 row per item of interest (as indicated by the column nameitem_fk
(="Item-FK"
). - that "last" is defined by the column
ts
(=timestamp
) - which isNOT NULL
. (Might alternatively beid
.) - that
(item_fk, ts)
is definedUNIQUE
(else you need moreORDER BY
expressions to make it deterministic)
Then this should be fastest and simplest:
SELECT i.* -- or what you need from item
FROM item i
JOIN LATERAL (
SELECT ARRAY (
SELECT status
FROM tbl t
WHERE t.itemfk = i.item_id
ORDER BY ts DESC
LIMIT 2
) AS last2_stat
) t ON t.last2_stat = '{PENDING, EXPIRED}'::text;
db<>fiddle here
You can return columns from table item
directly.
It is simple to adapt to any other terminal sequence of statuses.
Have this index for best performance:
CREATE INDEX ON tbl (itemfk, id DESC, status);
status
as last index expression only if you get index-only scans out of it.
Related, with more explanation:
- Optimize GROUP BY query to retrieve latest record per user
- Select first row in each GROUP BY group?
- What is the difference between LATERAL and a subquery in PostgreSQL?
- Why is array_agg() slower than the non-aggregate ARRAY() constructor?
Assuming ..
- an
item
table with exactly 1 row per item of interest (as indicated by the column nameitem_fk
(="Item-FK"
). - that "last" is defined by the column
ts
(=timestamp
) - which isNOT NULL
. (Might alternatively beid
.) - that
(item_fk, ts)
is definedUNIQUE
(else you need moreORDER BY
expressions to make it deterministic)
Then this should be fastest and simplest:
SELECT i.* -- or what you need from item
FROM item i
JOIN LATERAL (
SELECT ARRAY (
SELECT status
FROM tbl t
WHERE t.itemfk = i.item_id
ORDER BY ts DESC
LIMIT 2
) AS last2_stat
) t ON t.last2_stat = '{PENDING, EXPIRED}'::text;
db<>fiddle here
You can return columns from table item
directly.
It is simple to adapt to any other terminal sequence of statuses.
Have this index for best performance:
CREATE INDEX ON tbl (itemfk, id DESC, status);
status
as last index expression only if you get index-only scans out of it.
Related, with more explanation:
- Optimize GROUP BY query to retrieve latest record per user
- Select first row in each GROUP BY group?
- What is the difference between LATERAL and a subquery in PostgreSQL?
- Why is array_agg() slower than the non-aggregate ARRAY() constructor?
answered Dec 24 '18 at 4:19
Erwin BrandstetterErwin Brandstetter
95.2k9185300
95.2k9185300
add a comment |
add a comment |
Thanks for contributing an answer to Database Administrators Stack Exchange!
- 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%2fdba.stackexchange.com%2fquestions%2f225650%2ffind-all-items-where-the-2-latest-statuses-meet-condition%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
1
Is "last" defined by
timestamp
orID
? Is there also a tableitems
with with 1 row per item of interest (like "FK" seems to suggest)? And how many rows per item on average?– Erwin Brandstetter
Dec 24 '18 at 3:42