K emulaci analytických funkcí můžete využít uživatelské proměnné MySQL. (Existují také některé další přístupy, jako je použití semi-join nebo použití korelovaného dílčího dotazu. Mohu poskytnout řešení i pro ně, pokud si myslíte, že by mohly být vhodnější.)
Chcete-li emulovat analytickou funkci „součet“, zkuste něco takového:
SELECT t.user_id
, t.starttime
, t.order_number
, IF(t.order_number IS NOT NULL,
@tot_dur := 0,
@tot_dur := @tot_dur + t.visit_duration_seconds) AS tot_dur
FROM visit t
JOIN (SELECT @tot_dur := 0) d
ORDER BY t.user_id, t.start_time
„Trik“ spočívá v použití funkce IF k testování, zda order_number
je nulový. Když je null, přidáme do proměnné hodnotu trvání, jinak proměnnou nastavíme na nulu.
Používáme inline zobrazení (jinak d
, abyste zajistili, že proměnná @tot_dur bude inicializována na nulu.
POZNÁMKA:Při používání uživatelských proměnných MySQL, jako je tato, buďte opatrní. Ve výše uvedeném příkazu SELECT dochází k přiřazení proměnných v seznamu SELECT po příkazu ORDER BY, takže můžeme získat deterministické chování.
Tento dotaz nezpracovává "přerušení" v user_id. Abychom to získali, budeme potřebovat hodnotu user_id z předchozího řádku. Můžeme to zachovat v jiné uživatelské proměnné. Pořadí operací je deterministické a musíme se postarat o to, abychom provedli akumulaci PŘED přepsáním user_id z předchozího řádku.
Buď musíme změnit pořadí sloupců tak, aby se user_id objevilo za tot_dur (nebo zahrnout druhou kopii sloupce user_id)
SELECT t.user_id
, t.starttime
, t.order_number
, IF(t.order_number IS NULL,
@tot_dur := IF(@prev_user_id = t.user_id,@tot_dur,0) + t.visit_duration_seconds,
@tot_dur := 0
) AS tot_dur
, @prev_user_id := t.user_id AS prev_user_id
FROM visit t
JOIN (SELECT @tot_dur := 0, @prev_user_id := NULL) d
ORDER BY t.user_id, t.start_time
Hodnoty vrácené v user_id
a prev_user_id
sloupce jsou identické. Tento sloupec „navíc“ lze odstranit nebo lze změnit pořadí sloupců zabalením dotazu (jako vložené zobrazení) do jiného dotazu, i když to stojí za výkon:
SELECT v.user_id
, v.starttime
, v.order_number
, v.tot_dur
FROM (SELECT t.starttime
, t.order_number
, IF(t.order_number IS NULL,
@tot_dur := IF(@prev_user_id = t.user_id,@tot_dur,0) + t.visit_duration_seconds,
@tot_dur := 0
) AS tot_dur
, @prev_user_id := t.user_id AS user_id
FROM visit t
JOIN (SELECT @tot_dur := 0, @prev_user_id := NULL) d
ORDER BY t.user_id, t.start_time
) v
Tento dotaz ukazuje, že je možné, aby MySQL vracela zadanou sadu výsledků. Ale pro optimální výkon bychom chtěli spustit pouze dotaz ve vloženém zobrazení (s aliasem v
) a zpracovat změnu pořadí sloupců (umístěním sloupce user_id na první místo) na straně klienta, když se načítají řádky.
Další dva běžné přístupy využívají semi-join a korelovaný poddotaz, ačkoli tyto přístupy mohou být náročnější na zdroje při zpracování velkých sad.