Usando várias funções de janela diferentes e duas subconsultas, isso deve funcionar decentemente rápido:
WITH events(id, event, ts) AS (
VALUES
(1, 12, '2014-03-19 08:00:00'::timestamp)
,(2, 12, '2014-03-19 08:30:00')
,(3, 13, '2014-03-19 09:00:00')
,(4, 13, '2014-03-19 09:30:00')
,(5, 12, '2014-03-19 10:00:00')
)
SELECT first_value(pre_id) OVER (PARTITION BY grp ORDER BY ts) AS pre_id
, id, ts
, first_value(post_id) OVER (PARTITION BY grp ORDER BY ts DESC) AS post_id
FROM (
SELECT *, count(step) OVER w AS grp
FROM (
SELECT id, ts
, NULLIF(lag(event) OVER w, event) AS step
, lag(id) OVER w AS pre_id
, lead(id) OVER w AS post_id
FROM events
WINDOW w AS (ORDER BY ts)
) sub1
WINDOW w AS (ORDER BY ts)
) sub2
ORDER BY ts;
Usando
ts
como nome para a coluna de carimbo de data/hora.Supondo
ts
ser único - e indexado (uma restrição exclusiva faz isso automaticamente). Em um teste com uma tabela da vida real com 50 mil linhas, foi necessário apenas uma única verificação de índice . Então, deve ser decentemente rápido mesmo com mesas grandes. Em comparação, sua consulta com join / distinct não terminou após um minuto (como esperado).
Mesmo uma versão otimizada, lidando com uma junção cruzada de cada vez (a junção esquerda com quase nenhuma condição limitante é efetivamente uma cross join) não terminou após um minuto.
Para obter o melhor desempenho com uma mesa grande, ajuste suas configurações de memória, especialmente para
work_mem
(para grandes operações de classificação). Considere configurá-lo (muito) mais alto para sua sessão temporariamente, se você puder poupar a RAM. Leia mais aqui e aqui. Como?
-
Na subconsultasub1
olhe para o evento da linha anterior e mantenha-o apenas se tiver mudado, marcando assim o primeiro elemento de um novo grupo. Ao mesmo tempo, obtenha oid
da linha anterior e da próxima (pre_id
,post_id
).
-
Na subconsultasub2
,count()
conta apenas valores não nulos. Ogrp
resultante marca pares em blocos de mesmos eventos consecutivos.
-
NoSELECT
final , pegue o primeiropre_id
e o últimopost_id
por grupo para cada linha para chegar ao resultado desejado.
Na verdade, isso deve ser ainda mais rápido noSELECT
externo :
last_value(post_id) OVER (PARTITION BY grp ORDER BY ts RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS post_id
... já que a ordem de classificação da janela está de acordo com a janela parapre_id
, portanto, apenas uma única classificação é necessária. Um teste rápido parece confirmar isso. Mais sobre esta definição de quadro.
SQL Fiddle.