2010-09-26 16 views
8

Ho il seguente modello di Django (mappato alla tabella 'A'):interrogazione Django - "caso in cui" con la funzione di aggregazione

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

voglio eseguire la seguente query semplice in cima:

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 ... 

Sto cercando di evitare il raw SQL - Come può essere implementato con django (nell'esempio sopra sto usando avg, ma la stessa domanda è rilevante anche per max, min, somma ecc.)?

Ho provato ad utilizzare in più e di aggregazione:

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

e

aggregate(Avg('avg_field')), 

ma la funzione di aggregazione funziona solo con i campi del modello in modo che il campo aggiuntivo non può essere utilizzato qui. Come si può fare con django?

Grazie per l'aiuto

risposta

2

cosa si può fare che comunque permetterà di utilizzare Django set di query è qualcosa di simile:

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

utilizzare il risultato:

qs[0]["avg_field"] 

E questo permetterebbe la necessaria funzionalità.

2

Per quanto ne so non v'è (purtroppo) non c'è modo di fare ciò che lei ha descritto senza ricorrere a SQL crudo.

Detto questo, è un modo per calcolare la media nel modo in cui descrivi se sei disposto a denormalizzare i dati un po '. Ad esempio, è possibile aggiungere una nuova colonna denominata average_field che viene automaticamente impostata sul valore appropriato su save(). È possibile ignorare lo save() o toccare un segnale per farlo automaticamente. Ad es.

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) 

Una volta eseguita questa operazione, l'interrogazione diventa molto semplice.

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

Grazie. Purtroppo non posso memorizzare questi valori nella tabella del database poiché i valori del "caso in cui" possono variare in base al calcolo che voglio eseguire. – Lin

+0

@Li: capito. Posso capire perché questo non funzionerebbe in quel caso. –

+0

Grazie ancora per l'aiuto. Alla fine usare valori extra + mi ha dato il risultato che volevo. – Lin

Problemi correlati