patternsqlModerate
Select rows and add their previous rows
Viewed 0 times
rowsaddpreviousandselecttheir
Problem
I have a table structure that holds historical data for another table. Something like
I want to make a query that gets each row, and for each row, also get the previous row (latest date before the rows date) with the same id, e.g. something like
Is this possible? This is a pretty general question, but in my specific case, I am using PostgreSQL, so if there is some postgres features that accomplishes this, that is fine.
id | name | date
----------------------------------------
1 | John | 2016-01-01 14:38.123123
1 | John Smith | 2016-01-03 16:46.123123
2 | ... | 2016-01-01 14:38.123123
3 | ... | 2016-01-01 14:38.123123I want to make a query that gets each row, and for each row, also get the previous row (latest date before the rows date) with the same id, e.g. something like
SELECT id, name, date, previous.id, previous.name, previous.date
FROM ..?Is this possible? This is a pretty general question, but in my specific case, I am using PostgreSQL, so if there is some postgres features that accomplishes this, that is fine.
Solution
This is exactly what the
There is also the option of using a
An index on
LAG() window function does, available since version 8.4:SELECT id, name, date,
LAG(name) OVER (PARTITION BY id ORDER BY date) AS previous_name,
LAG(date) OVER (PARTITION BY id ORDER BY date) AS previous_date
FROM table_name ;There is also the option of using a
LATERAL join (available from 9.3). Might be better if you want all the columns (of the previous row). This assumes that there is a UNIQUE constraint on (id, date):SELECT t.id, t.name, t.date,
p.name AS previous_name, p.date AS previous_date
FROM table_name AS t
LEFT JOIN LATERAL
( SELECT p.*
FROM table_name AS p
WHERE p.id = t.id
AND p.date < t.date
ORDER BY p.date DESC
LIMIT 1
) AS p ON TRUE ;An index on
(id, date) would be helpful for both queries.Code Snippets
SELECT id, name, date,
LAG(name) OVER (PARTITION BY id ORDER BY date) AS previous_name,
LAG(date) OVER (PARTITION BY id ORDER BY date) AS previous_date
FROM table_name ;SELECT t.id, t.name, t.date,
p.name AS previous_name, p.date AS previous_date
FROM table_name AS t
LEFT JOIN LATERAL
( SELECT p.*
FROM table_name AS p
WHERE p.id = t.id
AND p.date < t.date
ORDER BY p.date DESC
LIMIT 1
) AS p ON TRUE ;Context
StackExchange Database Administrators Q#140475, answer score: 10
Revisions (0)
No revisions yet.