Mysql
 sql >> Base de données >  >> RDS >> Mysql

Calculer les médianes de plusieurs colonnes dans la même table en un seul appel de requête

Ce genre de chose est une grosse douleur dans le cou de MySQL. Il serait peut-être judicieux d'utiliser l'édition gratuite d'Oracle Express ou de postgreSQL si vous envisagez d'effectuer ce travail de classement statistique. Ils ont tous MEDIAN(value) fonctions d'agrégat qui sont intégrées ou disponibles en tant qu'extensions. Voici un petit sqlfiddle démontrant cela. http://sqlfiddle.com/#!4/53de8/6/0

Mais vous n'avez rien demandé à ce sujet.

Dans MySQL, votre problème de base est la portée de variables comme @rownum. Vous avez également un problème de pivot :c'est-à-dire que vous devez transformer les lignes de votre requête en colonnes.

Abordons d'abord le problème du pivot. Ce que vous allez faire, c'est créer une union de plusieurs grosses requêtes. Par exemple :

SELECT 'median_wages' AS tag, wages AS value
  FROM (big fat query making median wages) A
 UNION
SELECT 'median_volunteer_hours' AS tag, hours AS value
  FROM (big fat query making median volunteer hours) B
 UNION
SELECT 'median_solvent_days' AS tag, days AS value
  FROM (big fat query making median solvency days) C

Voici donc vos résultats dans un tableau de couples tag/valeur. Vous pouvez faire pivoter ce tableau comme ceci, pour obtenir une ligne avec une valeur dans chaque colonne.

SELECT SUM( CASE tag WHEN 'median_wages' THEN value ELSE 0 END 
          ) AS median_wages, 
SELECT SUM( CASE tag WHEN 'median_volunteer_hours' THEN value ELSE 0 END
          ) AS median_volunteer_hours, 
SELECT SUM( CASE tag WHEN 'median_solvent_days' THEN value ELSE 0 END 
          ) AS median_solvent_days
FROM (
    /* the above gigantic UNION query */
 ) Q

C'est ainsi que vous faites pivoter les lignes (de la requête UNION dans ce cas) vers les colonnes. Voici un tutoriel sur le sujet. http://www.artfulsoftware.com/infotree/qrytip.php?id =523

Nous devons maintenant nous attaquer aux sous-requêtes de calcul médian. Le code dans votre question semble assez bon. Je n'ai pas vos données donc il m'est difficile de les évaluer.

Mais vous devez éviter de réutiliser la variable @rownum. Appelez-le @rownum1 dans l'une de vos requêtes, @rownum2 dans la suivante, et ainsi de suite. Voici un petit violon sql qui ne fait qu'un d'entre eux. http://sqlfiddle.com/#!2/2f770/1/0

Maintenant, construisons un peu, en faisant deux médianes différentes. Voici le violon http://sqlfiddle.com/#!2/2f770/2/ 0 et voici la requête UNION. Avis la seconde moitié de la requête union utilise @rownum2 au lieu de @rownum .

Enfin, voici la requête complète avec le pivotement. http://sqlfiddle.com/#!2/2f770/13/0

 SELECT SUM( CASE tag WHEN 'Boston' THEN value ELSE 0 END ) AS Boston,
           SUM( CASE tag WHEN 'Bronx' THEN value ELSE 0 END ) AS Bronx   
   FROM (
 SELECT 'Boston' AS tag, pop AS VALUE
  FROM (
        SELECT @rownum := @rownum +1 AS  `row_number` , pop
          FROM pops, 
        (SELECT @rownum :=0)r
          WHERE pop >0 AND city = 'Boston'
          ORDER BY pop
        ) AS ordered_rows, 
        ( 
         SELECT COUNT( * ) AS total_rows
           FROM pops
          WHERE pop >0 AND city = 'Boston'
        ) AS rowcount
  WHERE ordered_rows.row_number = FLOOR( total_rows /2 ) +1
  UNION ALL
 SELECT 'Bronx' AS tag, pop AS VALUE
  FROM (
        SELECT @rownum2 := @rownum2 +1 AS  `row_number` , pop
          FROM pops, 
        (SELECT @rownum2 :=0)r
          WHERE pop >0 AND city = 'Bronx'
          ORDER BY pop
        ) AS ordered_rows, 
        ( 
         SELECT COUNT( * ) AS total_rows
           FROM pops
          WHERE pop >0 AND city = 'Bronx'
        ) AS rowcount
  WHERE ordered_rows.row_number = FLOOR( total_rows /2 ) +1
) D

Ce ne sont que deux médianes. Il vous en faut cinq. Je pense qu'il est facile de faire valoir que ce calcul médian est absurdement difficile à faire dans MySQL en une seule requête.