U kunt gebruik maken van MySQL-gebruikersvariabelen om analytische functies te emuleren. (Er zijn ook enkele andere benaderingen, zoals het gebruik van een semi-join of het gebruik van een gecorreleerde subquery. Ik kan hier ook oplossingen voor bieden, als u denkt dat ze geschikter zijn.)
Probeer iets als dit om een analytische functie "lopend totaal" te emuleren:
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
De "truc" hier is om een ALS-functie te gebruiken om te testen of order_number
is niets. Als het null is, voegen we de duurwaarde toe aan de variabele, anders stellen we de variabele in op nul.
We gebruiken een inline-weergave (alias d
, om ervoor te zorgen dat de variabele @tot_dur wordt geïnitialiseerd op nul.
OPMERKING:Wees voorzichtig met het gebruik van MySQL-gebruikersvariabelen zoals deze. In de SELECT-instructie zoals hierboven, vindt de toewijzing van de variabelen in de SELECT-lijst plaats na de ORDER BY, zodat we deterministisch gedrag kunnen krijgen.
Die query verwerkt geen "breaks" in user_id. Om dat te krijgen, hebben we de waarde van user_id uit de vorige rij nodig. We kunnen dat in een andere gebruikersvariabele bewaren. De volgorde van de bewerkingen is bepalend en we moeten ervoor zorgen dat de accumulatie wordt uitgevoerd VOORDAT we de user_id uit de vorige rij overschrijven.
We moeten ofwel de kolommen opnieuw ordenen zodat user_id verschijnt na tot_dur (of een tweede kopie van de user_id kolom opnemen)
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
De waarden die worden geretourneerd in de user_id
en prev_user_id
kolommen is identiek. Die "extra" kolom kan worden verwijderd of de kolommen kunnen opnieuw worden gerangschikt door de zoekopdracht (als een inline-weergave) in een andere zoekopdracht te plaatsen, hoewel dit prestatiekosten met zich meebrengt:
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
Die query laat zien dat het mogelijk is voor MySQL om de opgegeven resultatenset te retourneren. Maar voor optimale prestaties willen we alleen de query uitvoeren in de inline-weergave (alias v
), en de volgorde van de kolommen afhandelen (door de kolom user_id eerst te plaatsen) aan de clientzijde, wanneer de rijen worden opgehaald.
De andere twee veelvoorkomende benaderingen zijn het gebruik van een semi-join en het gebruik van een gecorreleerde subquery, hoewel deze benaderingen meer middelen kunnen vergen bij het verwerken van grote sets.