107

In PostgreSQL I run a query on it with several conditions that returns multiple rows, ordered by one of the columns. Example:

SELECT <some columns> 
FROM mytable
<maybe some joins here>
WHERE <various conditions>
ORDER BY date DESC

How would one get the first and the last row from this query?

3
  • 3
    Use the aggregate functions MIN & MAX: postgresql.org/docs/8.2/static/tutorial-agg.html Commented Sep 28, 2009 at 4:24
  • 2
    @rexem: min & max will not work on several columns - and they will work on single column only if you're ordering by this column. Commented Sep 28, 2009 at 4:57
  • 1
    You might also want to take a look at SELECT DISTINCT ON (...) ... ORDER BY .... See PostgreSQL documentation. Commented Aug 11, 2016 at 9:42

16 Answers 16

159

[Caveat: Might not be the most efficient way to do it]:

(SELECT <some columns>
FROM mytable
<maybe some joins here>
WHERE <various conditions>
ORDER BY date DESC
LIMIT 1)

UNION ALL

(SELECT <some columns>
FROM mytable
<maybe some joins here>
WHERE <various conditions>
ORDER BY date ASC    
LIMIT 1)
Sign up to request clarification or add additional context in comments.

5 Comments

I think the 'Top' keyword is for SQL server only, MySQL/Postgre uses 'Limit'
Using UNION ALL will make this marginally faster, as it removes a check for duplicates. It'll differ in how it works if the first and last row are the same of course - UNION will return just one row, UNION ALL will return the same row twice.
@Magnus Hagander: I'm not sure it will be any faster when there is at most 2 rows. Granted, I would normally make the distinction between UNION and UNION ALL.
Running the query as it is here gives me syntax error near UNION, possibly because there must be just one limit and order by's. I solved it wrapping the queries with parenthesis, like (SELECT ... LIMIT 1) UNION ALL (SELECT ... LIMIT 1)
Can anyone explain why this may not be efficient?
47

You might want to try this, could potentially be faster than doing two queries:

select <some columns>
from (
    SELECT <some columns>,
           row_number() over (order by date desc) as rn,
           count(*) over () as total_count
    FROM mytable
    <maybe some joins here>
    WHERE <various conditions>
) t
where rn = 1
   or rn = total_count
ORDER BY date DESC

Comments

23

First record:

SELECT <some columns> FROM mytable
<maybe some joins here>
WHERE <various conditions>
ORDER BY date ASC
LIMIT 1

Last record:

SELECT <some columns> FROM mytable
<maybe some joins here>
WHERE <various conditions>
ORDER BY date DESC
LIMIT 1

1 Comment

The UNION ALL method mentioned in the other comment will definitely be faster than issuing two queries.
18

last record :

SELECT * FROM `aboutus` order by id desc limit 1

first record :

SELECT * FROM `aboutus` order by id asc limit 1

2 Comments

That is invalid SQL for PostgreSQL (it uses standard double quotes " for quoting object names - and they aren't needed at all here anyway)
@souleiman Each query is as fast as it can be. The query planner will use the appropriate index and return as fast as possible O(log(N))... however doing this in 2 separate queries will be slower and/or less efficient than one query if you always want both the first and last record as the OP indicated. Just use UNION ALL (faster) between the 2 queries (or UNION if you don't want duplicates).
18

In all the exposed ways of do until now, must go through scan two times, one for the first row and one for the last row.

Using the Window Function "ROW_NUMBER() OVER (...)" plus "WITH Queries", you can scan only one time and get both items.

Window Function: https://www.postgresql.org/docs/9.6/static/functions-window.html

WITH Queries: https://www.postgresql.org/docs/9.6/static/queries-with.html

Example:

WITH scan_plan AS (
SELECT
    <some columns>,
    ROW_NUMBER() OVER (ORDER BY date DESC) AS first_row, /*It's logical required to be the same as major query*/
    ROW_NUMBER() OVER (ORDER BY date ASC) AS last_row /*It's rigth, needs to be the inverse*/
FROM mytable
<maybe some joins here>
WHERE <various conditions>
ORDER BY date DESC)

SELECT
    <some columns>
FROM scan_plan
WHERE scan_plan.first_row = 1 OR scan_plan.last_row = 1;

On that way you will do relations, filtrations and data manipulation only one time.

Try some EXPLAIN ANALYZE on both ways.

2 Comments

thanks for also giving the references to the key concepts
the count(*) over () as total_count above is a little bit more performant, because it uses just one WindowAgg and the dataset is sorted only once as well.
8
SELECT <rows> FROM TABLE_NAME WHERE ROWID=(SELECT MIN(ROWID) FROM TABLE_NAME) 
UNION
SELECT <rows> FROM TABLE_NAME WHERE ROWID=(SELECT MAX(ROWID) FROM TABLE_NAME)

or

SELECT * FROM TABLE_NAME WHERE ROWID=(SELECT MIN(ROWID) FROM TABLE_NAME) 
                            OR ROWID=(SELECT MAX(ROWID) FROM TABLE_NAME)

2 Comments

PostgreSQL does not have a rowid, it's called ctid there (and neither Oracle's rowid nor PostgreSQL's ctid guarantee any ordering)
Why not make this simpler: SELECT * FROM TABLE_NAME WHERE rowid=(SELECT MIN(rowid) FROM TABLE_NAME) OR rowid=(SELECT MAX(rowid) FROM TABLE_NAME)
6

I know this is a 7 year old thread, but the question was nearly identical and the accepted answer was what I started this with and eventually optimized to the following, which in my case returns consistently 85ms +-5ms with <some_column> being an indexed int field.

note1: the UNION ALL example in the accepted answer works too but was less performant in my case coming in at 300ms +-20ms.

note2: the next most upvoted answer (the row counter example) also works but was the least performant in my case coming in at 800ms +-70ms.

select
  (select <some_column> from <some_table>
    order by <some_field> limit 1)        as oldest,
  (select <some_column> from <some_table> 
    order by <some_field> desc limit 1)   as newest
;

I did note that op referenced possible joins. I haven't had the need to include joins for my own purposes (just getting the current low and high IDs in fairly dynamic view) but with this model, the subqueries for oldest and newest should be able to be full fledged queries. Haven't tested, so not sure if it would work or be optimal.

I did test this model (which may also have already been suggested above) which might be a bit easier to join against, but the performance as-is was just a bit less than half of the example above, consistently returning 220ms +-10ms in my case.

select oldest.<some_field> as old, 
       newest.<some_field> as new  
from
  (select <some_column> from <some_table>
    order by <some_field> limit 1)        as oldest,
  (select <some_column> from <some_table> 
    order by <some_field> desc limit 1)   as newest
;

1 Comment

So which of the two code snippets was the one that was the 85ms?
4

WINDOW functions FIRST_VALUE() and LAST_VALUE() are useful in some cases. The key benefits - this query is readeable, sorts data only once and it is only one query for several columns.

 SELECT
    FIRST_VALUE(timestamp) over w as created_dt,
    LAST_VALUE(timestamp) over w as last_update_dt,
    LAST_VALUE(action) over w as last_action
FROM events
WINDOW w as (ORDER BY timestamp ASC)

For instance, it can be used to get first and last rows by some ID

SELECT DISTINCT
    order_id,
    FIRST_VALUE(timestamp) over w as created_dt,
    LAST_VALUE(timestamp) over w as last_update_dt,
    LAST_VALUE(action) over w as last_action
FROM events as x
WINDOW w as (PARTITION BY order_id ORDER BY timestamp ASC)

4 Comments

To avoid duplicating window functions, you can use WINDOW clause for each windowing behavior and then reference it in OVER: WINDOW w as (PARTITION BY order_id ORDER BY timestamp ASC)
I'm not sure this actually does quite what you think it does. In SQL Server, if you have a table with columns timestamp and action, your first query generates one row for every row in events. On the first row, created_dt is the first timestamp as expected, but last_update_dt is always equal to created_dt. That is, it's the last value seen to that point in the data retrieval. Perhaps PostGreSQL is different
@auspex Looks like you are talking about you specific data. Or the fieldname makes misunderstanding. SELECT FIRST_VALUE(create_dt) over w as created_dt_first, LAST_VALUE(create_dt) over w as created_dt_last, FIRST_VALUE(last_update_dt) over w2 as last_update_dt_first, LAST_VALUE(last_update_dt) over w2 as last_update_dt_last FROM events WINDOW w as (ORDER BY create_dt ASC), w2 as (ORDER BY last_update_dt ASC)
I'm definitely not talking about my specific data. I'm talking about what may be a difference between what Postgres does and what SQL Server does. On SQL Server, your query won't work correctly, so I was just checking that Postgres does not in fact implement LAST_VALUE the same way.
1
select *
from {Table_Name}
where {x_column_name}=(
    select d.{x_column_name} 
    from (
        select rownum as rno,{x_column_name}
        from {Table_Name})d
        where d.rno=(
            select count(*)
            from {Table_Name}));

Comments

1
-- Create a function that always returns the first non-NULL item
CREATE OR REPLACE FUNCTION public.first_agg ( anyelement, anyelement )
RETURNS anyelement LANGUAGE SQL IMMUTABLE STRICT AS $$
        SELECT $1;
$$;


-- And then wrap an aggregate around it
CREATE AGGREGATE public.FIRST (
        sfunc    = public.first_agg,
        basetype = anyelement,
        stype    = anyelement
);

-- Create a function that always returns the last non-NULL item
CREATE OR REPLACE FUNCTION public.last_agg ( anyelement, anyelement )
RETURNS anyelement LANGUAGE SQL IMMUTABLE STRICT AS $$
        SELECT $2;
$$;

-- And then wrap an aggregate around it
CREATE AGGREGATE public.LAST (
        sfunc    = public.last_agg,
        basetype = anyelement,
        stype    = anyelement
);

Got it from here: https://wiki.postgresql.org/wiki/First/last_(aggregate)

Comments

1

The correct Sql listed below

SELECT * FROM (SELECT city, length(city) FROM station WHERE LENGTH(city)=(SELECT MIN(LENGTH(city)) FROM station) ORDER BY city ) LIMIT 1;

SELECT * FROM (SELECT city, length(city) FROM station WHERE LENGTH(city)=(SELECT MAX(LENGTH(city)) FROM station) ORDER BY city ) LIMIT 1; 

Comments

0
SELECT 
    MIN(Column), MAX(Column), UserId 
FROM 
    Table_Name
WHERE 
    (Conditions)
GROUP BY 
    UserId DESC

or

SELECT        
    MAX(Column) 
FROM            
    TableName
WHERE        
    (Filter)

UNION ALL

SELECT        
    MIN(Column)
FROM            
    TableName AS Tablename1
WHERE        
    (Filter)
ORDER BY 
    Column

Comments

0

Why not use order by asc limit 1 and the reverse, order by desc limit 1?

Comments

0

How to get the First and Last Record of DB in c#.

SELECT TOP 1 * 
  FROM ViewAttendenceReport 
 WHERE EmployeeId = 4 
   AND AttendenceDate >='1/18/2020 00:00:00' 
   AND AttendenceDate <='1/18/2020 23:59:59'
 ORDER BY Intime ASC
 UNION
SELECT TOP 1 * 
  FROM ViewAttendenceReport 
 WHERE EmployeeId = 4 
   AND AttendenceDate >='1/18/2020 00:00:00' 
   AND AttendenceDate <='1/18/2020 23:59:59' 
 ORDER BY OutTime DESC; 

1 Comment

That's SQL, not C#. It's also not Postgres
0

I think this code gets the same and is easier to read.

SELECT <some columns> 
FROM mytable
<maybe some joins here>
WHERE date >= (SELECT date from mytable)
OR date <= (SELECT date from mytable);

1 Comment

While this code may answer the question, providing additional context regarding why and/or how this code answers the question improves its long-term value.
-1

In SQL Server, we may utilize this codes to get the first and last row of dataset.

For First Row,

select top 1 *
from Sales

For Last Row,

select top 1  x.*
from 
(select *, Row_Number() over(order by Order_No) as rank
from Sales) x
order by rank desc

1 Comment

TOP 1 doesn't work in postgres! only LIMIT 1

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.