sql >> Database teknologi >  >> RDS >> PostgreSQL

Hent værdier fra første og sidste række pr. gruppe

Der er forskellige enklere og hurtigere måder.

2x DISTINCT ON

SELECT *
FROM  (
   SELECT DISTINCT ON (name)
          name, week AS first_week, value AS first_val
   FROM   tbl
   ORDER  BY name, week
   ) f
JOIN (
   SELECT DISTINCT ON (name)
          name, week AS last_week, value AS last_val
   FROM   tbl
   ORDER  BY name, week DESC
   ) l USING (name);

Eller kortere:

SELECT *
FROM  (SELECT DISTINCT ON (1) name, week AS first_week, value AS first_val FROM tbl ORDER BY 1,2) f
JOIN  (SELECT DISTINCT ON (1) name, week AS last_week , value AS last_val  FROM tbl ORDER BY 1,2 DESC) l USING (name);

Enkel og let at forstå. Også hurtigst i mine gamle tests. Detaljeret forklaring på DISTINCT ON :

  • Vælg første række i hver GROUP BY-gruppe?

2x vinduesfunktion, 1x DISTINCT ON

SELECT DISTINCT ON (name)
       name, week AS first_week, value AS first_val
     , first_value(week)  OVER w AS last_week
     , first_value(value) OVER w AS last_value
FROM   tbl t
WINDOW w AS (PARTITION BY name ORDER BY week DESC)
ORDER  BY name, week;

Det eksplicitte WINDOW klausul forkorter kun koden, ingen effekt på ydeevnen.

first_value() af sammensat type

De samlede funktioner min() eller max() accepterer ikke sammensatte typer som input. Du bliver nødt til at oprette tilpassede aggregerede funktioner (hvilket ikke er så svært).
Men vinduesfunktionerne first_value() og last_value() gør . Med udgangspunkt i det kan vi udtænke simple løsninger:

Simpel forespørgsel

SELECT DISTINCT ON (name)
       name, week AS first_week, value AS first_value
     ,(first_value((week, value)) OVER (PARTITION BY name ORDER BY week DESC))::text AS l
FROM   tbl t
ORDER  BY name, week;

Outputtet har alle data, men værdierne for den sidste uge er fyldt i en anonym post (valgfrit castet til text ). Du har muligvis brug for dekomponerede værdier.

Dekomponeret resultat med opportunistisk brug af tabeltype

Til det har vi brug for en velkendt komposittype. En tilpasset tabeldefinition ville give mulighed for opportunistisk brug af selve tabeltypen direkte:

CREATE TABLE tbl (week int, value int, name text);  -- optimized column order

week og value kom først, så nu kan vi sortere efter selve tabeltypen:

SELECT (l).name, first_week, first_val
     , (l).week AS last_week, (l).value AS last_val
FROM  (
   SELECT DISTINCT ON (name)
          week AS first_week, value AS first_val
        , first_value(t) OVER (PARTITION BY name ORDER BY week DESC) AS l
   FROM   tbl t
   ORDER  BY name, week
   ) sub;

Dekomponeret resultat fra brugerdefineret rækketype

Det er nok ikke muligt i de fleste tilfælde. Registrer en sammensat type med CREATE TYPE (permanent) eller med CREATE TEMP TABLE (i løbet af sessionen):

CREATE TEMP TABLE nv(last_week int, last_val int);  -- register composite type
SELECT name, first_week, first_val, (l).last_week, (l).last_val
FROM (
   SELECT DISTINCT ON (name)
          name, week AS first_week, value AS first_val
        , first_value((week, value)::nv) OVER (PARTITION BY name ORDER BY week DESC) AS l
   FROM   tbl t
   ORDER  BY name, week
   ) sub;

Tilpassede aggregerede funktioner first() &last()

Opret funktioner og aggregater én gang pr. database:

CREATE OR REPLACE FUNCTION public.first_agg (anyelement, anyelement)
  RETURNS anyelement
  LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE AS
'SELECT $1;'

CREATE AGGREGATE public.first(anyelement) (
  SFUNC = public.first_agg
, STYPE = anyelement
, PARALLEL = safe
);


CREATE OR REPLACE FUNCTION public.last_agg (anyelement, anyelement)
  RETURNS anyelement
  LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE AS
'SELECT $2';

CREATE AGGREGATE public.last(anyelement) (
  SFUNC = public.last_agg
, STYPE = anyelement
, PARALLEL = safe
);

Så:

SELECT name
     , first(week) AS first_week, first(value) AS first_val
     , last(week)  AS last_week , last(value)  AS last_val
FROM  (SELECT * FROM tbl ORDER BY name, week) t
GROUP  BY name;

Nok den mest elegante løsning. Hurtigere med det ekstra modul first_last_agg leverer en C-implementering.
Sammenlign instruktionerne i Postgres Wiki.

Relateret:

  • Beregning af følgervækst over tid for hver influencer

db<>spil her (viser alle)
Gamle sqlfiddle

Hver af disse forespørgsler var væsentligt hurtigere end det aktuelt accepterede svar i en hurtig test på en tabel med 50.000 rækker med EXPLAIN ANALYZE .

Der er flere måder. Afhængigt af datadistribution kan forskellige forespørgselsstile være (meget) hurtigere, men alligevel. Se:

  • Optimer GROUP BY-forespørgsel for at hente seneste række pr. bruger


  1. Træstruktur i sql i Oracle. Sådan viser du træ, underordnede noder og overordnede noder i SQL Oracle

  2. 6 grunde til, at Microsoft Access kan hjælpe din virksomhed

  3. Advarsel:PDO::__construct():[2002] Ingen sådan fil eller mappe (forsøger at oprette forbindelse via unix:///tmp/mysql.sock) i

  4. Hvad er den ideelle datatype at bruge, når du gemmer breddegrad/længdegrad i en MySQL-database?