sql >> Databáze >  >> RDS >> PostgreSQL

Funkce okna PostgreSQL:rozdělení podle srovnání

Pomocí několika různých funkcí okna a dvou dílčích dotazů by to mělo fungovat slušně rychle:

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;

Pomocí ts jako název pro sloupec časového razítka.
Za předpokladu ts být jedinečný – a indexovaný (jedinečné omezení to dělá automaticky).

V testu se skutečnou tabulkou s 50 000 řádky potřeboval pouze jediné prohledání indexu . Takže by měl být slušně rychlý i u velkých stolů. Pro srovnání, váš dotaz s připojením / odlišným neskončil po minutě (jak se očekávalo).
Dokonce i optimalizovaná verze, která se zabývá jedním křížovým spojením najednou (levé spojení s téměř omezující podmínkou je ve skutečnosti omezené cross join) neskončil po minutě.

Pro nejlepší výkon s velkým stolem vylaďte nastavení paměti, zejména pro work_mem (pro velké operace řazení). Zvažte dočasné nastavení (mnohem) vyšší pro vaši relaci, pokud můžete ušetřit RAM. Přečtěte si více zde a zde.

Jak?

  1. V dílčím dotazu sub1 podívejte se na událost z předchozího řádku a ponechte ji, pouze pokud se změnila, čímž označíte první prvek nové skupiny. Zároveň získejte id předchozího a následujícího řádku (pre_id , post_id ).

  2. V dílčím dotazu sub2 , count() počítá pouze nenulové hodnoty. Výsledný grp označuje partnery v blocích po sobě jdoucích stejných událostí.

  3. Ve finále SELECT , vezměte první pre_id a poslední post_id na skupinu pro každý řádek, abyste dosáhli požadovaného výsledku.
    Ve skutečnosti by to mělo být ještě rychlejší ve vnějším SELECT :

     last_value(post_id) OVER (PARTITION BY grp ORDER BY ts
                               RANGE BETWEEN UNBOUNDED PRECEDING
                                     AND     UNBOUNDED FOLLOWING) AS post_id
    

    ... protože pořadí řazení okna souhlasí s oknem pro pre_id , takže je potřeba pouze jeden druh. Zdá se, že to potvrzuje rychlý test. Více o této definici rámce.

SQL Fiddle.




  1. 10 tipů a triků pro správu efektivní databáze

  2. Jak AKTUALIZOVAT jeden sloupec pomocí jiného sloupce v jiné tabulce? Chyba SQL:ORA-00933:Příkaz SQL nebyl správně ukončen

  3. Jak REGEXP funguje v MariaDB

  4. Jak monitorovat stav instancí databáze