三个级别选择性能问题?

时间:2018-06-01 15:32:35

标签: sql postgresql select

我对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)

是否有可能简化这样的查询,因为我想要返回一行,如上例所示?

三层嵌套选择会导致性能问题吗?

2 个答案:

答案 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

这是有效的,因为布尔值的排序为FalseTrue

因此,w1reason = 'START'的所有行放在顶部,并在该组内按ts排序。我们使用ts在第一行中选择FIRST_VALUE。同样,我们得到ts

的最后一个reason = 'STOP'

这会检测到以下情况:

  1. 在时间范围内找不到用户的STARTSTOP个原因(没有返回任何行)
  2. 在时间范围内为用户找到STARTSTOP(但不是两者)原因(行以1为空返回)
  3. 第一个START ts位于最后一个STOP ts之后(例如,如果按ts排序的最后一行只有一个reason = 'START'(比较返回的行start&amp; stop
  4. START ts&lt;正如所料STOP ts。 (返回行,两列都正确填充)