Есть три уровня выбора производительности? - PullRequest
0 голосов
/ 01 июня 2018

Я довольно новичок в 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 еще нетpresent:

           start           | stop           
---------------------------+------
 2018-06-01 10:44:55.52+01 | 
(1 row)

Можно ли упростить такой запрос, если я хочу вернуть одну строку, как в приведенных выше примерах?

Будут ли три уровня вложенных выборок вызывать проблемы с производительностью?

Ответы [ 2 ]

0 голосов
/ 03 июня 2018

использовать оконные функции, чтобы вообще не вкладывать

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'

Это обнаруживает следующие случаи:

  1. Нет START или STOP причин были найдены для пользователя во времядиапазон (строки не возвращаются)
  2. Либо START, либо STOP (но не обе) причины были найдены для пользователя во временном диапазоне (строка возвращается с нулевым значением)
  3. ПервыйSTART ts - после последней STOP ts (например, если самая последняя строка, упорядоченная ts, является единственной, где reason = 'START' (сравнение значений start & stop возвращенной строки
  4. START ts <<code>STOP ts, как и ожидалось (строка возвращается с правильно заполненными обоими столбцами)
0 голосов
/ 03 июня 2018

Это выглядит довольно хорошо для меня.внутренний выбор, являющийся пределом 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'
...