Há muitas maneiras de fazer isso com as funções existentes. Você pode usar o funções de janela
first_value()
e last_value()
, combinado com DISTINCT
ou DISTINCT ON
para obtê-lo sem junções e subconsultas:SELECT DISTINCT ON (userid)
userid
, last_value(rank) OVER w
- first_value(rank) OVER w AS rank_delta
FROM rankings
WINDOW w AS (PARTITION BY userid ORDER BY ts
ROWS BETWEEN UNBOUNDED PRECEDING
AND UNBOUNDED FOLLOWING);
Observe os quadros personalizados para as funções da janela !
Ou você pode usar funções agregadas básicas em uma subconsulta e JOIN:
SELECT userid, r2.rank - r1.rank AS rank_delta
FROM (
SELECT userid
, min(ts) AS first_ts
, max(ts) AS last_ts
FROM rankings
GROUP BY 1
) sub
JOIN rankings r1 USING (userid)
JOIN rankings r2 USING (userid)
WHERE r1.ts = first_ts
AND r2.ts = last_ts;
Assumindo exclusivo
(userid, rank)
, ou seus requisitos seriam ambíguos. Demonstração do SQL Fiddle.
Shichinin no samurai
Por solicitação nos comentários, o mesmo para apenas as últimas sete linhas por ID de usuário (ou quantos puderem ser encontrados, se houver menos):
Novamente, uma das muitas maneiras possíveis. Mas acredito que este seja um dos mais curtos:
SELECT DISTINCT ON (userid)
userid
, first_value(rank) OVER w
- last_value(rank) OVER w AS rank_delta
FROM rankings
WINDOW w AS (PARTITION BY userid ORDER BY ts DESC
ROWS BETWEEN CURRENT ROW AND 7 FOLLOWING)
ORDER BY userid, ts DESC;
Observe a ordem de classificação invertida. A primeira linha é a entrada "mais recente". Eu abro um quadro de (máx.) 7 linhas e escolho apenas os resultados para a entrada mais recente com
DISTINCT ON
. Demonstração do SQL Fiddle.