Vous pouvez utiliser des variables utilisateur MySQL pour émuler des fonctions analytiques. (Il existe également d'autres approches, comme l'utilisation d'une semi-jointure ou l'utilisation d'une sous-requête corrélée. Je peux également fournir des solutions pour celles-ci, si vous pensez qu'elles peuvent être plus appropriées.)
Pour émuler une fonction analytique "total cumulé", essayez quelque chose comme ceci :
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
Le "truc" ici est d'utiliser une fonction IF pour tester si oui ou non order_number
est nul. Lorsqu'elle est nulle, nous ajoutons la valeur de durée à la variable, sinon, nous définissons la variable sur zéro.
Nous utilisons une vue en ligne (aliasée d
, pour s'assurer que la variable @tot_dur est initialisée à zéro.
REMARQUE :Faites attention lorsque vous utilisez des variables utilisateur MySQL comme celle-ci. Dans l'instruction SELECT comme ci-dessus, l'affectation des variables dans la liste SELECT se produit après ORDER BY, nous pouvons donc obtenir un comportement déterministe.
Cette requête ne gère pas les "ruptures" dans user_id. Pour obtenir cela, nous allons avoir besoin de la valeur de user_id de la ligne précédente. Nous pouvons conserver cela dans une autre variable utilisateur. L'ordre des opérations est déterministe, et nous devons prendre soin de faire l'accumulation AVANT d'écraser l'user_id de la ligne précédente.
Nous devons soit réorganiser les colonnes afin que user_id apparaisse après tot_dur (ou inclure une deuxième copie de la colonne 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
Les valeurs retournées dans le user_id
et prev_user_id
colonnes est identique. Cette colonne "supplémentaire" pourrait être supprimée, ou les colonnes pourraient être réorganisées en enveloppant la requête (en tant que vue intégrée) dans une autre requête, bien que cela ait un coût en termes de performances :
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
Cette requête démontre qu'il est possible pour MySQL de renvoyer le jeu de résultats spécifié. Mais pour des performances optimales, nous voudrions exécuter uniquement la requête dans la vue en ligne (alias v
) et gérer la réorganisation des colonnes (en plaçant la colonne user_id en premier) côté client, lorsque les lignes sont récupérées.
Les deux autres approches courantes utilisent une semi-jointure et une sous-requête corrélée, bien que ces approches puissent être plus gourmandes en ressources lors du traitement de grands ensembles.