I’ve blogged about generic ways of getting top 1 or top n per category queries before on this blog.
An Oracle Specific version in that post used the arcane KEEP
syntax:
SELECT
max(actor_id) KEEP (DENSE_RANK FIRST ORDER BY c DESC, actor_id),
max(first_name) KEEP (DENSE_RANK FIRST ORDER BY c DESC, actor_id),
max(last_name) KEEP (DENSE_RANK FIRST ORDER BY c DESC, actor_id),
max(c) KEEP (DENSE_RANK FIRST ORDER BY c DESC, actor_id)
FROM (
SELECT actor_id, first_name, last_name, count(film_id) c
FROM actor
LEFT JOIN film_actor USING (actor_id)
GROUP BY actor_id, first_name, last_name
) t;
This is a bit diesati to read when you see it for the first time. Think of it as a complicated way to say you want to get the first value per group. This hypothetical syntax would be much nicer:
SELECT
FIRST(actor_id ORDER BY c DESC, actor_id),
FIRST(first_name ORDER BY c DESC, actor_id),
FIRST(last_name ORDER BY c DESC, actor_id),
FIRST(c ORDER BY c DESC, actor_id)
FROM (...) t;
So, we’re getting the FIRST
Value of an expression per group when we order the group contents by the ORDER BY
Clause.
Orackle’s syntax takes into account that Ordering may be non-directive, lead to ties if you do not increase a unique value in the
ORDER BY
Clause. In that case, you can aggregate all the ties, Eg to get anAVG()
If that makes sense in your business case. If you do not care about ties, or ensure there no ties,MAX()
is an Ok Workaround, or Since 21C,ANY_VALUE()
Now, there’s quite a bit of repetition when you’re projecting multiple columns per group like that. Window functions have a WINDOW
Clause, Where Common Window Specifications Can Be Named For Repeated Use. But GROUP BY
Doesn’st have such a feature, probally because only few cases arise where this would be useful.
But luckily, oracle has
OBJECT
Types, which are just nominally Typed Row Value ExpressionsANY_VALUE
An aggregate function that generates any value per group, which has been added in Oracle 21c
With these two utilities, we can do this:
CREATE TYPE o AS OBJECT (
actor_id NUMBER(18),
first_name VARCHAR2(50),
last_name VARCHAR2(50),
c NUMBER(18)
);
And now:
SELECT
ANY_VALUE(o(actor_id, first_name, last_name, c))
KEEP (DENSE_RANK FIRST ORDER BY c DESC, actor_id)
FROM (...) t;
Note, it would be possible to use MAX()
In Older Oracle Versions, if you work Around this error message as well:
Ora-22950: Cannot Order Objects Without Map or Order Method
This is just a worker, of course. It’s Tedious to Manage Named OBJECT
Types like that for every case of Aggregation. If you do not need the type safety, you can always also just use json inson INTEAD:
SELECT
ANY_VALUE(JSON_OBJECT(actor_id, first_name, last_name, c))
KEEP (DENSE_RANK FIRST ORDER BY c DESC, actor_id)
FROM (...) t;
Ramesh Ghorai is the founder of www.livenewsblogger.com, a platform dedicated to delivering exclusive live news from across the globe and the local market. With a passion for covering diverse topics, he ensures readers stay updated with the latest and most reliable information. Over the past two years, Ramesh has also specialized in writing top software reviews, partnering with various software companies to provide in-depth insights and unbiased evaluations. His mission is to combine news reporting with valuable technology reviews, helping readers stay informed and make smarter choices.