MySQL réutilise certains alias

Sep 01 2020

J'ai actuellement une requête dans laquelle je fais deux sous-requêtes pour obtenir des données X, Y:

SELECT
  t.series AS week,
  ( ... ) X,
  ( ..., AND ... ) Y,
  ROUND(( ... ) * 100) / ( ..., AND ... ), 2) Z
FROM series_tmp t

Y est une sorte de sous-ensemble de X, puisque j'applique juste une condition supplémentaire à celles existantes, si X est:

SELECT COUNT(*)
FROM t1
INNER JOIN t2
ON t2.id = t1.another_id
WHERE t2.something = 1
AND t1.date BETWEEN t.series AND t.series + INTERVAL 6 DAY

Alors Y a une condition AND supplémentaire:

SELECT COUNT(*)
FROM t1
INNER JOIN t2
ON t2.id = t1.another_id
WHERE t2.something = 1
AND t1.date BETWEEN t.series AND t.series + INTERVAL 6 DAY
AND t1.some_state = 'x state'

Et pour la valeur de XI, il faut prendre ces deux résultats - X et Y et faire un calcul. Comme je ne peux pas utiliser les alias, je dois utiliser une sous-requête, non? Mais dans ce cas, cela semble trop.

Existe-t-il un moyen de réutiliser ces sous-requêtes? Cela semble trop être la même chose.

J'utilise MySQL 5.6 donc je ne peux pas utiliser les CTE :(

PS: series_tmpvient de cette merveilleuse idée (grâce à eux).

Réponses

2 Solarflare Sep 06 2020 at 07:23

Le standard SQL n'autorise pas la réutilisation de l'alias ici.

Cependant, MySQL utilise une extension du standard qui vous permet de faire quelque chose de proche, à savoir utiliser un alias préalablement défini dans une sous-requête:

select some_expression as alias_name, (select alias_name)

ce qui dans votre cas signifierait que vous pouvez faire

SELECT
   t.series AS week,
   ( ... ) X,
   ( ... AND ... ) Y,
   ROUND( (select X) * 100) / (select Y), 2) Z
FROM series_tmp t

L'alias ne doit pas correspondre à un nom de colonne, car cela aurait la priorité et cela ne fonctionnera pas pour les agrégats. Semblable à la réponse de nbk , ceci est spécifique à MySQL et ne fonctionnera pas dans d'autres systèmes de base de données.

Une solution qui fonctionne également dans d'autres systèmes de bases de données et que vous, selon votre commentaire, déjà trouvée, serait d'introduire une table dérivée, par exemple

select week, X, Y,  ROUND(X * 100 / Y, 2) Z
from (
  SELECT
    t.series AS week,
    ( ... ) X,
    ( ..., AND ... ) Y
  FROM series_tmp t
) as sub
3 nbk Sep 01 2020 at 18:38

Pour cela, vous pouvez utiliser des variables définies par l'utilisateur

SELECT 
    t.series AS week,
    @x:=(SELECT 
            COUNT(*)
        FROM
            t1
                INNER JOIN
            t2 ON t2.id = t1.another_id
        WHERE
            t2.something = 1
                AND t1.date BETWEEN t.series AND t.series + INTERVAL 6 DAY) X,
    @y:=(SELECT 
            COUNT(*)
        FROM
            t1
                INNER JOIN
            t2 ON t2.id = t1.another_id
        WHERE
            t2.something = 1
                AND t1.date BETWEEN t.series AND t.series + INTERVAL 6 DAY
                AND t1.some_state = 'x state') Y,
    ROUND((@x * 100) / @y, 2) Z
FROM
    series_tmp t

Si vous souhaitez utiliser vos alias, MySQL vous permet uniquement de les utiliser dans ORDER BY, GROUP BYetHAVING

Comme exemple, vous pouvez utiliser ORDER BY X DESC,Y ASCouHAVING X > 0.5

2 Akina Sep 07 2020 at 14:54

Y est une sorte de sous-ensemble de X, puisque j'applique juste une condition supplémentaire à celles existantes, si X est:

SELECT COUNT(*)
FROM t1
INNER JOIN t2
ON t2.id = t1.another_id
WHERE t2.something = 1
AND t1.date BETWEEN t.series AND t.series + INTERVAL 6 DAY

Alors Y a une condition AND supplémentaire:

SELECT COUNT(*)
FROM t1
INNER JOIN t2
ON t2.id = t1.another_id
WHERE t2.something = 1
AND t1.date BETWEEN t.series AND t.series + INTERVAL 6 DAY
AND t1.some_state = 'x state'

Dans ce cas particulier, je vous recommande d'utiliser l'agrégation conditionnelle:

SELECT COUNT(*) AS total_count,
       SUM(t1.some_state = 'x state') AS x_state_count
FROM t1
INNER JOIN t2
  ON t2.id = t1.another_id
WHERE t2.something = 1
  AND t1.date BETWEEN t.series AND t.series + INTERVAL 6 DAY

Si la condition t1.some_state = 'x state'pour une ligne particulière est TRUE(qui est un alias de 1dans MySQL) alors SUM()s'ajoutera 1à la somme intermédiaire pour cette ligne.

Si la condition t1.some_state = 'x state'pour une ligne particulière est FALSE(qui est un alias de 0dans MySQL) alors SUM()ajoutera 0(ie ne modifiera pas la somme intermédiaire) pour cette ligne.

En conséquence, le nombre de lignes correspondantes sera calculé.


Dans la plupart des cas simples, une telle agrégation conditionnelle au lieu d'une réutilisation d'alias suffit.