我对Postgres很新,我正在尝试实现一些复杂的功能。
以下查询有效:
select (
select ts as start
from (
select *
from events
where (reason = 'START' or reason = 'STOP')
and ts < ?
and user = ?
order by ts desc
limit 1
) as subquery_start
where reason = 'START'
),(
select ts as stop
from (
select *
from events
where (reason = 'START' or reason = 'STOP')
and ts > ?
and user = ?
order by ts
limit 1
) as subquery_stop
where reason = 'STOP'
);
它一次查找用户是否在START和STOP事件之间,返回:
start | stop
---------------------------+---------------------------
2018-06-01 10:44:55.52+01 | 2018-06-01 10:45:07.52+01
(1 row)
是否不是:
start | stop
-------+------
|
(1 row)
或者他们是否只是在START之后,以及之后的STOP还没有出现:
start | stop
---------------------------+------
2018-06-01 10:44:55.52+01 |
(1 row)
是否有可能简化这样的查询,因为我想要返回一行,如上例所示?
三层嵌套选择会导致性能问题吗?
答案 0 :(得分:1)
对我来说看起来很不错。内部选择限制1将使用您在'user,ts'列对上的任何索引
此查询的最佳索引将打开
events(user,ts) where (reason = 'START' or reason = 'STOP')
但在events(user,ts)
上应该差不多。
可能更清楚地获得类似结果
WITH subquery_before AS (
select ts,reason
from events
where (reason = 'START' or reason = 'STOP')
and ts < ?
and user = ?
order by ts desc
limit 1
),
subquery_after AS (
select ts,reason
from events
where (reason = 'START' or reason = 'STOP')
and ts > ?
and user = ?
order by ts
limit 1
)
SELECT
subquery_before.ts AS start,
subquery_after.ts AS stop
WHERE subquery_before.reason = 'START'
AND subquery_after.reason = 'STOP'
答案 1 :(得分:0)
使用窗口函数来完全避免嵌套
SELECT
CASE WHEN FIRST_VALUE(reason) OVER w1 = 'START'
THEN FIRST_VALUE(ts) OVER w1
ELSE NULL
END start,
CASE WHEN LAST_VALUE(reason) OVER w2 = 'STOP'
THEN LAST_VALUE(ts) OVER w2
ELSE NULL
END stop
FROM events
WHERE (reason = 'START' or reason = 'STOP')
AND ts > ? AND ts < ?
AND user = ?
WINDOW w1 AS (ORDER BY reason != 'START', ts)
w2 AS (ORDER BY reason = 'STOP', ts)
LIMIT 1
这是有效的,因为布尔值的排序为False
,True
。
因此,w1
将reason = 'START'
的所有行放在顶部,并在该组内按ts排序。我们使用ts
在第一行中选择FIRST_VALUE
。同样,我们得到ts
reason = 'STOP'
这会检测到以下情况:
START
或STOP
个原因(没有返回任何行)START
或STOP
(但不是两者)原因(行以1为空返回)START
ts
位于最后一个STOP
ts
之后(例如,如果按ts排序的最后一行只有一个reason = 'START'
(比较返回的行start
&amp; stop
值START
ts
&lt;正如所料STOP
ts
。 (返回行,两列都正确填充)