我知道这是一个常见的问题,我已经对它进行了一些阅读。我想要的是一种高性能的方式(在一个查询中最好),用于接收基于引用行id的下一个和上一个行id。我在stackoverflow找到了很多问题和答案,还有一个非常好的答案https://stackoverflow.com/a/15992856/1230358。我所拥有的是基于这个主题的答案。
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)
)
使用引用id=2
查询完全返回我需要的结果(第一行是前一个id,第二行是下一个id):
id
--
1
--
3
问题是,如果查询边缘情况id=1
或id=max(id)
,结果会错过上一个
或下一行id,因为根本没有前一行或下一行。结果现在只有一行,如果这是前一个我们的下一行id,则不清楚。
id
--
2 (next value)
但是,我需要这样的结果
id
--
NULL (or 0 - previous value)
--
2 (next value)
我需要的是一个基于或类似于上层查询的解决方案,它使用NULL值(或0)填充不存在的边缘框ID。由于我使用支持不同dbms的webframework来结果,因此它应该与 mysql
,sqlite
和postgres
一起使用。它应该使用以下模式:
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');
更新
可能的解决方案是:
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
答案 0 :(得分:1)
select
lag(id) over (order by starts_on) as previous,
lead(id) over (order by starts_on) as next
from test_1
where id = 2
答案 1 :(得分:0)
在一般情况下,要组合两个查询的结果,您可以将它们用作子查询,并将它们分为两列(scalar subqueries):
SELECT (SELECT ...) AS a, (SELECT ...) AS b;
或分为两行:
SELECT * FROM (SELECT ...
UNION ALL
SELECT NULL
LIMIT 1)
UNION ALL
SELECT * FROM (SELECT ...
UNION ALL
SELECT NULL
LIMIT 1);
(SELECT NULL LIMIT 1
构造确保在实际查询不返回行时返回NULL。)
答案 2 :(得分:0)
与许多现代数据库一样,Postgres与MySQL相比,支持Analytic,Window函数,也称为OLAP函数。
这里你想要的是分析LEAD()和LAG()函数的组合。你需要将它们与COALESCE()函数结合起来,据我所知,PostGres不支持NVL()或者IFNULL(),如果你想要其他东西而不是NULL。如果您使用的是starts_on
和ends_on
日期,请参阅starts_on
的示例。
SELECT
COALESCE(LAG(starts_on) OVER (ORDER BY starts_on),'1900-01-01 00:00:00')
AS neighbour_starts_on
FROM test_1
WHERE starts_on = '2017-01-07 00:00:00'
UNION ALL SELECT
COALESCE(LEAD(starts_on) OVER (ORDER BY starts_on),'9999-12-31 23:59:59')
AS neighbour_starts_on
FROM test_1
WHERE starts_on = '2017-01-07 00:00:00'