2010-09-26 8 views
8

ich habe folgendes django-Modell (abgebildet auf den Tisch ‚A‘):Django Abfrage - „Fall, wenn“ mit Aggregationsfunktion

select avg(case 
     when (value > 0 and value <= 50) then 0 
     when (value > 50 and value < 70) then 50 
     else 100 end) 
from A 
where ... 
:

class A(models.Model): 
    name = models.CharField(max_length=64, null=False) 
    value = models.IntegerField() 
    ... 

ich folgende einfache Abfrage auf ausführen möchten

Ich versuche, rohe SQL zu vermeiden - Wie kann dies mit Django implementiert werden (im obigen Beispiel verwende ich avg, aber die gleiche Frage ist auch relevant für max, min, Summe usw.)?

habe ich versucht, mit extra und Aggregate:

extra(select={'avg_field': case_when_query}) 

und

aggregate(Avg('avg_field')), 

aber die Aggregatfunktion funktioniert nur mit Modellfeldern so dass das zusätzliche Feld hier nicht verwendet werden kann. Wie kann das mit Django gemacht werden?

Danke für die Hilfe

Antwort

2

Was getan werden kann, die uns noch erlauben django queryset ist so etwas zu verwenden:

qs = A.objects.extra(select={"avg_field": 
        "avg(case when...)"}).filter(...).values("avg_field") 

das Ergebnis zu benutzen:

qs[0]["avg_field"] 

Und die benötigt würde es ermöglichen Funktionalität.

2

Soweit ich weiß, gibt es (leider) keine Möglichkeit, was Sie ohne Rückgriff auf rohe SQL beschrieben zu tun.

Das sagte dort ist eine Möglichkeit, den Durchschnitt zu berechnen, wie Sie beschreiben, wenn Sie bereit sind, Ihre Daten ein wenig zu denormalisieren. Sie können beispielsweise eine neue Spalte mit dem Namen average_field hinzufügen, die automatisch auf den entsprechenden Wert unter save() gesetzt wird. Sie können entweder save() überschreiben oder ein Signal antippen, um dies automatisch zu tun. Für z.B.

class A(models.Model): 
    name = models.CharField(max_length=64, null=False) 
    value = models.IntegerField() 
    average_field = models.IntegerField(default = 0) 

    def _get_average_field(self): 
     # Trying to match the case statement's syntax. 
     # You can also do 0 < self.value <= 50 
     if self.value > 0 and self.value <= 50: 
      return 0 
     elif self.value > 50 and self.value < 70: 
      return 50 
     else: 
      return 100 

    def save(self, *args, **kwargs): 
     if self.value: 
      self.average_field = self._get_average_field() 
     super(A, self).save(*args, **kwargs) 

Sobald Sie dies tun, wird Ihre Abfrage sehr einfach.

A.objects.filter(...).aggregate(avg = Avg('average_field')) 
+0

Dank. Leider kann ich diese Werte nicht in der Datenbanktabelle speichern, da die "case when" -Werte abhängig von der Berechnung, die ich ausführen möchte, variieren können. – Lin

+0

@Li: verstanden. Ich kann sehen, warum das in diesem Fall nicht funktionieren würde. –

+0

Danke nochmal für die Hilfe. Irgendwann mit extra + Werten hat mir das Ergebnis gebracht, das ich wollte. – Lin