Je suppose que vous disposez d'une table data constituée de 2 colonnes semaine et duree. Pour calculer la médiane sous Oracle vous n'avez plus qu'à utiliser la requête suivante :
SELECT semaine,MEDIAN(duree) FROM data GROUP BY semaine;
Pour MySQL c'est beaucoup plus compliqué puisqu'on ne dispose pas de cette magnifique fonction. J'ai donc fait quelques recherches et je suis tombé sur les travaux de frédéric Brouard. J'ai pu trouver la procédure adéquate basée sur la solution de Chris Date.
En supposant que vous disposez toujours de la table data qui chez moi utilise le moteur CSV, la méthode est la suivante :
CREATE TABLE d AS SELECT * FROM data;
ALTER TABLE d ADD COLUMN id INT AUTO_INCREMENT PRIMARY KEY FIRST, ADD INDEX (semaine,duree) ;
SELECT semaine,round(AVG(duree),2) AS MEDIANE
FROM (
SELECT semaine,MIN(duree) AS duree
-- valeurs au dessus
FROM
(
SELECT ST1.semaine,ST1.id, ST1.duree
FROM d AS ST1
INNER JOIN d AS ST2
ON ST1.duree <= ST2.duree AND ST1.semaine=ST2.semaine
GROUP BY ST1.id, ST1.duree
HAVING COUNT(*) <= (SELECT CEILING(COUNT(*) / 2.0)
FROM d d2 where d2.semaine=ST1.semaine)
) SUR
GROUP BY semaine
UNION
SELECT semaine,MAX(duree) AS duree
-- valeurs en dessous
FROM
(
SELECT ST1.semaine, ST1.duree
FROM d AS ST1
INNER JOIN d AS ST2
ON ST1.duree >= ST2.duree AND ST1.semaine=ST2.semaine
GROUP BY ST1.id, ST1.duree
HAVING COUNT(*) <= (SELECT CEILING(COUNT(*) / 2.0)
FROM d d2 where d2.semaine=ST1.semaine)
) SOU
GROUP BY semaine
) T
GROUP BY semaine;
DROP TABLE d;
ALTER TABLE d ADD COLUMN id INT AUTO_INCREMENT PRIMARY KEY FIRST, ADD INDEX (semaine,duree) ;
SELECT semaine,round(AVG(duree),2) AS MEDIANE
FROM (
SELECT semaine,MIN(duree) AS duree
-- valeurs au dessus
FROM
(
SELECT ST1.semaine,ST1.id, ST1.duree
FROM d AS ST1
INNER JOIN d AS ST2
ON ST1.duree <= ST2.duree AND ST1.semaine=ST2.semaine
GROUP BY ST1.id, ST1.duree
HAVING COUNT(*) <= (SELECT CEILING(COUNT(*) / 2.0)
FROM d d2 where d2.semaine=ST1.semaine)
) SUR
GROUP BY semaine
UNION
SELECT semaine,MAX(duree) AS duree
-- valeurs en dessous
FROM
(
SELECT ST1.semaine, ST1.duree
FROM d AS ST1
INNER JOIN d AS ST2
ON ST1.duree >= ST2.duree AND ST1.semaine=ST2.semaine
GROUP BY ST1.id, ST1.duree
HAVING COUNT(*) <= (SELECT CEILING(COUNT(*) / 2.0)
FROM d d2 where d2.semaine=ST1.semaine)
) SOU
GROUP BY semaine
) T
GROUP BY semaine;
DROP TABLE d;
Sous Oracle, la requête est très rapide et il n'est pas nécessaire de créer un index particulier. Par contre sous MySQL, si vous vous amusez à supprimer l'index vous le ressentirez très vite si vous disposez de beaucoup de données. Dans mon cas, ma table contient 4874 lignes. Sans l'index, la requête dure près de 5 minutes contre moins de 5 secondes une fois indexée.
Aucun commentaire:
Enregistrer un commentaire