sql >> Databáze >  >> RDS >> Mysql

Kumulativní součet za sadu řádků v mysql

AKTUALIZACE

MySQL 8.0 zavádí „funkce okna“, funkcionalitu ekvivalentní „funkcím okna“ SQL Server (s rozdělením a řazením, které poskytuje Transact-SQL OVER syntaxe) a Oracle „analytické funkce“.

Referenční příručka MySQL 12.21 Funkce okna https://dev.mysql .com/doc/refman/8.0/en/window-functions.html

Zde poskytnutá odpověď je přístup pro verze MySQL starší než 8.0.

PŮVODNÍ ODPOVĚĎ

MySQL neposkytuje typ analytické funkce, kterou byste použili k získání běžícího "kumulativního součtu", jako jsou analytické funkce dostupné v jiných DBMS (jako je Oracle nebo SQL Server.)

Je však možné emulovat některé analytické funkce pomocí MySQL.

Existují (alespoň) dva použitelné přístupy:

Jedním z nich je použití korelovaného poddotazu k získání mezisoučtu. Tento přístup může být drahý na velkých množinách a komplikovaný, pokud jsou predikáty na vnějším dotazu komplikované. Velmi záleží na tom, jak komplikované to "vícenásobné spojení na více stolech" je. (MySQL také bohužel nepodporuje CTE.)

Druhým přístupem je použití uživatelských proměnných MySQL, aby se provedlo nějaké kontrolní zpracování přerušení. „Trik“ je v tom, že výsledky z vašeho dotazu setřídí (pomocí příkazu ORDER BY) a poté se váš dotaz zabalí do jiného dotazu.

Uvedu příklad druhého přístupu.

Vzhledem k pořadí, ve kterém MySQL provádí operace, je cumulative_total sloupec je třeba vypočítat před hodnotou z id a day z aktuálního řádku se uloží do uživatelských proměnných. Nejjednodušší je umístit tento sloupec jako první.

Inline pohled s aliasem i (v dotazu níže) je zde pouze pro inicializaci uživatelských proměnných, pro případ, že jsou již nastaveny v relaci. Pokud již mají přiřazené hodnoty, chceme jejich aktuální hodnoty ignorovat a nejjednodušší způsob, jak to udělat, je inicializovat je.

Váš původní dotaz je zabalen do závorek a je mu přidělen alias c v příkladu níže. Jedinou změnou vašeho původního dotazu je přidání klauzule ORDER BY, takže si můžeme být jisti, že zpracujeme řádky z dotazu postupně.

Vnější výběr kontroluje, zda id a day hodnota z aktuálního řádku se "shoduje" s předchozím řádkem. Pokud ano, přidáme amount z aktuálního řádku na kumulativní mezisoučet. Pokud se neshodují, vynulujeme kumulativní mezisoučet a přidáme částku z aktuálního řádku (nebo jednodušeji přiřadíme částku z aktuálního řádku).

Poté, co jsme provedli výpočet kumulativního součtu, uložíme id a day hodnoty z aktuálního řádku do uživatelských proměnných, takže jsou dostupné, když zpracováváme další řádek.

Například:

SELECT IF(@prev_id = c.id AND @prev_day = c.day
         ,@cumtotal := @cumtotal + c.amount
         ,@cumtotal := c.amount) AS cumulative_total
     , @prev_id  := c.id  AS `id`
     , @prev_day := c.day AS `day`
     , c.hr
     , c.amount AS `amount'
  FROM ( SELECT @prev_id  := NULL
              , @prev_day := NULL
              , @subtotal := 0
       ) i
  JOIN (

         select id, day, hr, amount from
         ( //multiple joins on multiple tables)a
         left join
         (//unions on multiple tables)b
         on a.id=b.id

         ORDER BY 1,2,3
       ) c

Pokud je nutné vrátit sloupce v jiném pořadí s kumulativním součtem jako posledním sloupcem, pak jednou z možností je zabalit celý tento příkaz do sady závorek a použít tento dotaz jako vložené zobrazení:

SELECT d.id
     , d.day
     , d.hr
     , d.amount
     , d.cumulative_total
FROM (
       // query from above
     ) d


  1. Sériová čísla na skupinu řádků pro složený klíč

  2. Jak aktualizovat řádky s náhodným datem

  3. JSON_KEYS() – vrátí klíče z objektu JSON v MySQL

  4. php kód pro testování pdo je k dispozici?