I know this is a common question and I've done some reading on it. What I want is a performant way (best in one query) for receiving the the next and previous row id, based on a reference row id. I've found many questions and answers here at stackoverflow and one valuable thread with a really nice answer https://stackoverflow.com/a/15992856/1230358. What I have is based on the answers in this thread.
select id from test_1
where (
id = IFNULL((select max(id) from test_1 where id < 2 order by starts_on, id), 0)
or id = IFNULL((select min(id) from test_1 where id > 2 order by starts_on, id), 0)
)
Querying with the reference id=2
returns exactlythe result that I need (first row is the previous id, second row is the next id):
id
--
1
--
3
The problem is, if querying the edge cases id=1
or id=max(id)
, the results miss either the previous
or next row id, because there simply is no no previous or next row. The result has now only one row and it's not clear if this is either the previous our next row id.
id
--
2 (next value)
However, i need a result like this
id
--
NULL (or 0 - previous value)
--
2 (next value)
What I need is a solution based or similar in performance to the upper query, that fills the non-existant edgecases ids preferable with a NULL value (or 0). As I'm cunsuming the results with a webframework supporting different dbms, it should work with mysql
, sqlite
, and postgres
. It should work with the following schema:
drop table if exists test_1;
create table test_1 (id INTEGER PRIMARY KEY,starts_on DATETIME, ends_on DATETIME);
insert into test_1 (starts_on, ends_on) Values ('2017-01-01 00:00:00', '2017-01-01 00:00:00');
insert into test_1 (starts_on, ends_on) Values ('2017-01-01 00:00:00', '2017-01-01 00:00:00');
insert into test_1 (starts_on, ends_on) Values ('2017-01-01 00:00:00', '2017-01-01 00:00:00');
insert into test_1 (starts_on, ends_on) Values ('2017-01-01 00:00:00', '2017-01-01 00:00:00');
insert into test_1 (starts_on, ends_on) Values ('2017-01-01 00:00:00', '2017-01-01 00:00:00');
insert into test_1 (starts_on, ends_on) Values ('2017-01-01 00:00:00', '2017-01-01 00:00:00');
insert into test_1 (starts_on, ends_on) Values ('2017-01-01 00:00:00', '2017-01-01 00:00:00');
drop table if exists test_2;
create table test_2 (id INTEGER PRIMARY KEY,starts_on DATETIME, ends_on DATETIME);
insert into test_2 (starts_on, ends_on) Values ('2017-01-01 00:00:00', '2017-01-07 00:00:00');
insert into test_2 (starts_on, ends_on) Values ('2017-01-02 00:00:00', '2017-01-08 00:00:00');
insert into test_2 (starts_on, ends_on) Values ('2017-01-03 00:00:00', '2017-01-09 00:00:00');
insert into test_2 (starts_on, ends_on) Values ('2017-01-04 00:00:00', '2017-01-10 00:00:00');
insert into test_2 (starts_on, ends_on) Values ('2017-01-05 00:00:00', '2017-01-11 00:00:00');
insert into test_2 (starts_on, ends_on) Values ('2017-01-06 00:00:00', '2017-01-12 00:00:00');
insert into test_2 (starts_on, ends_on) Values ('2017-01-07 00:00:00', '2017-01-13 00:00:00');
drop table if exists test_3;
create table test_3 (id INTEGER PRIMARY KEY,starts_on DATETIME, ends_on DATETIME);
insert into test_3 (starts_on, ends_on) Values ('2017-01-01 00:00:00', '2017-01-07 00:00:00');
insert into test_3 (starts_on, ends_on) Values ('2017-01-02 00:00:00', '2017-01-08 00:00:00');
insert into test_3 (starts_on, ends_on) Values ('2017-01-02 00:00:00', '2017-01-09 00:00:00');
insert into test_3 (starts_on, ends_on) Values ('2017-01-04 00:00:00', '2017-01-10 00:00:00');
insert into test_3 (starts_on, ends_on) Values ('2017-01-05 00:00:00', '2017-01-11 00:00:00');
insert into test_3 (starts_on, ends_on) Values ('2017-01-07 00:00:00', '2017-01-12 00:00:00');
insert into test_3 (starts_on, ends_on) Values ('2017-01-07 00:00:00', '2017-01-13 00:00:00');
Update:
A possible solution would be:
select distinct
(select max(id) from test_1 where id < 7 order by starts_on, id) as prev,
(select min(id) from test_1 where id > 7 order by starts_on, id) as next
from test_1