Можете да използвате MySQL потребителски променливи, за да емулирате аналитични функции. (Има и някои други подходи, като използване на полуприсъединяване или използване на корелирана подзаявка. Мога да предоставя решения и за тях, ако смятате, че може да са по-подходящи.)
За емулиране на аналитична функция "изпълнена обща", опитайте нещо подобно:
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
"Тръкът" тук е да използвате функция IF, за да проверите дали order_number
е нула. Когато е нула, добавяме стойността на продължителността към променливата, в противен случай задаваме променливата на нула.
Използваме вграден изглед (с псевдоним като d
, за да се гарантира, че променливата @tot_dur е инициализирана на нула.
ЗАБЕЛЕЖКА:Внимавайте с използването на потребителски променливи на MySQL като тази. В оператора SELECT както по-горе, присвояването на променливите в списъка SELECT се случва след ORDER BY, така че можем да получим детерминирано поведение.
Тази заявка не обработва "прекъсвания" в user_id. За да получим това, ще ни трябва стойността на user_id от предишния ред. Можем да запазим това в друга потребителска променлива. Редът на операциите е детерминиран и трябва да се погрижим да извършим натрупването, ПРЕДИ да презапишем user_id от предишния ред.
Трябва или да пренаредим колоните, така че user_id да се появи след tot_dur (или да включим второ копие на колоната 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
Стойностите, върнати в user_id
и prev_user_id
колони е идентичен. Тази „допълнителна“ колона може да бъде премахната или колоните могат да бъдат пренаредени чрез обвиване на заявката (като вграден изглед) в друга заявка, въпреки че това идва с разходи за производителност:
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
Тази заявка показва, че е възможно MySQL да върне посочения набор от резултати. Но за оптимална производителност бихме искали да изпълним само заявката във вградения изглед (с псевдоним като v
) и обработва пренареждането на колоните (поставяйки първо колоната user_id) от страна на клиента, когато редовете бъдат извлечени.
Другите два често срещани подхода използват полусъединяване и използване на корелирана подзаявка, въпреки че тези подходи могат да бъдат по-интензивни при обработка на големи набори.