2012-01-30 2 views
3

J'ai une table qui ressemble à quelque chose comme ceci:MySQL - des alternatives aux sous-requêtes imbriquées en limitant les données agrégées dans une sous-requête corrélée

DataTable 
+------------+------------+------------+ 
| Date  | DailyData1 | DailyData2 | 
+------------+------------+------------+ 
| 2012-01-23 |  146.30 |  212.45 | 
| 2012-01-20 |  554.62 |  539.11 | 
| 2012-01-19 |  710.69 |  536.35 | 
+------------+------------+------------+ 

Je suis en train de créer une vue (appeler AggregateView) qui va, pour chaque date et pour chaque colonne de données, montrez quelques agrégats différents. Par exemple, select * from AggregateView where Date = '2012-01-23' pourrait donner:

+------------+--------------+----------------+--------------+----------------+ 
| Date  | Data1_MTDAvg | Data1_20DayAvg | Data2_MTDAvg | Data2_20DayAvg | 
+------------+--------------+----------------+--------------+----------------+ 
| 2012-01-23 |  697.71 |   566.34 |  601.37 |   192.13 | 
+------------+--------------+----------------+--------------+----------------+ 

Data1_MTDAvg montre avg(DailyData1) pour chaque jour en Janvier avant le 23 janvier et Data1_20DayAvg montre la même, mais pour les 20 précédentes dates dans le tableau. Je ne suis pas un ninja SQL, mais je pensais que la meilleure façon de le faire serait via des sous-requêtes. La moyenne MTD est facile:

select t1.Date, (select avg(t2.DailyData1) 
       from DataTable t2 
       where t2.Date <= t1.Date 
        and month(t2.Date) = month(t1.Date) 
        and year(t2.Date) = year(t1.Date)) Data1_MTDAvg 
from DataTable t1; 

Mais j'enferrons la moyenne de 20 jours en raison de la nécessité de limiter le nombre de résultats renvoyés. Notez que les dates dans la table sont irrégulières, donc je ne peux pas utiliser un intervalle de date; J'ai besoin des vingt derniers enregistrements de la table, plutôt que de tous les enregistrements des vingt derniers jours. La seule solution que j'ai trouvée est d'utiliser une sous-requête imbriquée pour limiter d'abord les enregistrements sélectionnés, puis prendre la moyenne.

seul, la sous-requête fonctionne pour les dates hardcoded individuelles:

select avg(t2.DailyData1) Data1_20DayAvg 
from (select DailyData1 
     from DataTable 
     where Date <= '2012-01-23' 
     order by Date desc 
     limit 0,20) t2; 

Mais en essayant d'intégrer cela dans le cadre de la plus grande requête explose:

select t1.Date, (select avg(t2.DailyData1) Data1_20DayAvg 
       from (select DailyData1 
         from DataTable 
         where Date <= t1.Date 
         order by Date desc 
         limit 0,20) t2) 
from DataTable t1; 
ERROR 1054 (42S22): Unknown column 't1.Date' in 'where clause' 

De la recherche autour J'ai l'impression que vous ne pouvez pas utiliser de sous-requêtes corrélées dans le cadre d'une clause from, ce qui, je pense, est le cas ici. L'autre problème est que je ne suis pas sûr que MySQL accepte une définition de vue contenant une clause from dans une sous-requête. Existe-t-il un moyen de limiter les données dans ma sélection agrégée sans recourir à des sous-requêtes, afin de contourner ces deux problèmes?

Répondre

1

Non, vous ne pouvez pas utiliser de sous-requêtes corrélées dans la clause FROM. Mais vous pouvez les utiliser dans les conditions ON:

SELECT AVG(d.DailyData1) Data1_20DayAvg 
     --- other aggregate stuff on d (Datatable) 
FROM 
     (SELECT '2012-01-23' AS DateChecked 
    ) AS dd 
    JOIN 
     DataTable AS d 
    ON 
     d.Date <= dd.DateChecked 
    AND 
     d.Date >= COALESCE( 
     (SELECT DailyData1 
     FROM DataTable AS last20 
     WHERE Date <= dd.DateChecked 
      AND (other conditions for last20) 
     ORDER BY Date DESC 
     LIMIT 1 OFFSET 19 
    ), '1001-01-01' ) 
WHERE (other conditions for d Datatable) 

similaires, pour de nombreuses dates:

SELECT dd.DateChecked 
    , AVG(d.DailyData1) Data1_20DayAvg 
     --- other aggregate stuff on d (Datatable) 
FROM 
     (SELECT DISTINCT Date AS DateChecked 
     FROM DataTable 
    ) AS dd 
    JOIN 
     DataTable AS d 
    ON 
     d.Date <= dd.DateChecked 
    AND 
     d.Date >= COALESCE( 
     (SELECT DailyData1 
     FROM DataTable AS last20 
     WHERE Date <= dd.DateChecked 
      AND (other conditions for last20) 
     ORDER BY Date DESC 
     LIMIT 1 OFFSET 19 
    ), '1001-01-01' ) 
WHERE (other conditions for d Datatable) 
GROUP BY 
     dd.DateChecked 

Les deux requêtes supposer que Datatable.Date a une contrainte UNIQUE.

Questions connexes