Is there any way to embed a flag in a select that indicates that it is the first or the last row of a result set? I'm thinking something to the effect of:
> SELECT is_first_row() AS f, is_last_row() AS l FROM blah;
f | l
-----------
t | f
f | f
f | f
f | f
f | t
The answer might be in window functions but I've only just learned about them, and I question their efficiency.
SELECT first_value(unique_column) OVER () = unique_column, last_value(unique_column) OVER () = unique_column, * FROM blah;
seems to do what I want. Unfortunately, I don't even fully understand that syntax, but since unique_column
is unique and NOT NULL
it should deliver unambiguous results. But if it does sorting, then the cure might be worse than the disease. (Actually, in my tests, unique_column
is not sorted, so that's something.)
EXPLAIN ANALYZE
doesn't indicate there's an efficiency problem, but when has it ever told me what I needed to know?
And I might need to use this in an aggregate function, but I've just been told window functions aren't allowed there.
It is simple using window functions with particular frames:
As you can see
count(*) over (rows between unbounded preceding and current row)
returns rows count from the data set beginning to current row andcount(*) over (rows between current row and unbounded following)
returns rows count from the current to data set end.1
indicates the first/last rows.It works until you ordering your data set by
order by
. In this case you need to duplicate it in the frames definitions:PS: As mentioned by a_horse_with_no_name in the comment:
In fact, Window Functions are a great approach and for that requirement of yours, they are awesome.
Regarding efficiency, window functions work over the data set already at hand. Which means the DBMS will just add extra processing to infer first/last values.
Just one thing I'd like to suggest: I like to put an
ORDER BY
criteria inside theOVER
clause, just to ensure the data set order is the same between multiple executions, thus returning the same values to you.You can use the
lead()
andlag()
window functions (over the appropiate window) and compare them to NULL:-- \i tmp.sql
Result:
[updated: added a randomly sorted case]
Try using
SELECT just cut half of the processing time. You can go for indexing also.