Reputation: 2184
Assuming the following table:
ID Name Revision
--- ----- --------
1 blah 0
2 yada 1
3 blah 1
4 yada 0
5 blah 2
6 blah 3
How do I get the two records, one for "blah" and one for "yada" with highest revision number (3 for blah and 1 for yada)? Something like:
ID Name Revision
--- ----- --------
6 blah 3
2 yada 1
Also, once these records are retrieved, how do I get the rest, ordered by name and revision?
I am trying to create a master-detail view where master records are latest revisions and details include the previous revisions.
Upvotes: 1
Views: 767
Reputation: 656391
Basically, with the aggregate function MAX()
:
SELECT "Name", MAX("Revision") AS max_revison
FROM tbl
WHERE "Name" IN ('blah', 'yada');
GROUP BY "Name"
ORDER BY "Name"; -- ordering by revision would be pointless;
If you need more columns from the row, there are several ways. One would be to join the above subquery back to the base table:
SELECT t.*
FROM (
SELECT "Name", max("Revision") AS max_revison
FROM tbl
WHERE "Name" IN ('blah', 'yada');
GROUP BY "Name"
) AS sub
JOIN tbl AS t ON t."Revision" = sub.max_revison
AND t."Name" = sub."Name"
ORDER BY "Name";
Generally, this has the potential to yield more than one row per "Name"
- if "Revision" is not unique (per "Name"). You would have to define how to pick one
from a group of peers sharing the same maximum "Revision" - a tiebreaker.
Another way would be with NOT EXISTS
, excluding rows that have greater peers, possibly faster:
SELECT t.*
FROM tbl AS t
WHERE "Name" IN ('blah', 'yada')
AND NOT EXISTS (
SELECT 1
FROM tbl AS t1
WHERE t1."Name" = t."Name"
AND t1."Revision" > t."Revision"
)
ORDER BY "Name";
Or you could use a CTE with an analytic function (window function):
WITH cte AS (
SELECT *, ROW_NUMBER() OVER(PARTITION BY "Name" ORDER BY "Revision" DESC) AS rn
FROM tbl
WHERE "Name" IN ('blah', 'yada')
)
SELECT *
FROM cte
WHERE rn = 1;
The last one is slightly different: one row per "Name"
is guaranteed. If you don't use more ORDER BY
items, an arbitrary row will be picked in case of a tie. If you want all peers use RANK()
instead.
Upvotes: 5
Reputation: 10525
In Oracle, you can use LAST function to simplify this.
select max(id) keep (dense_rank last order by revision),
name,
max(revision)
from table
group by name;
Upvotes: 1
Reputation: 9711
This approach will select the rows for each Name with the maximum revision number for that Name. The result will be the exact output you were looking for in your post.
SELECT *
FROM tbl a
WHERE a.revision = (select max(revision) from tbl where name = a.name)
ORDER BY a.name
Upvotes: 2