2017-03-01 2 views
0

Teil einer Funktion ist die Berechnung einer Anzahl der letzten gültigen Angebote, die niedriger, gleich und höher als a_price sind. Ich kann keinen besseren Weg finden, also benutze ich jetzt zwei Loops. Das Problem ist, dass hier 575 Abfragen ausgeführt werden, was zu viel ist.Wie kann die Anzahl (575) von Abfragen in einer verschachtelten Schleife verringert werden?

Ein Produkt kann viele Käufer haben und Käufer hat viele Angebote (mit unterschiedlichen Datumsangaben). Ich habe versucht, prefetch_related('buyers') hinzuzufügen, aber es hat überhaupt nicht geholfen.

EDIT: In diesem Fall gibt es 32 Produkte und jedes Produkt hat von 0 bis 30 Käufer.

reset_queries() 
    products = Product.objects.filter(user=user) 
    my_active_products = products.filter(active=True).prefetch_related('buyers') 

    for product in my_active_products: 
     for buyer in product.buyers.filter(valid=True): 
      last_valid_offer = buyer.get_last_valid_offer() 
      a_price = product.a_price 
      if a_price: 
       if a_price < last_valid_offer.eur_price: 
        cheaper += 1 
       elif a_price > last_valid_offer.eur_price: 
        more_expensive += 1 
       elif a_price == last_valid_offer.eur_price: 
        equal += 1 
      else: 
       unknown += 1 
    print len(connection.queries) 

Wissen Sie, was ich tun soll, um die Anzahl der Abfragen zu verringern?

EDIT Models.py:

class Product(Model): 
    name... 
    active = BooleanField(...) 

class Buyer(Model): 
    product = ForeignKey('Product',related_name='buyers') 


    def get_last_valid_offer(self): 
     return self.offers.filter(valid=True).latest('datetime') 

class Offer(Model): 
    buyer = ForeignKey('Buyer', related_name='offers') 
    valid = BooleanField(...) 
    datetime = DateTimeField(...) 
    eur_price = MoneyField(...) 

Antwort

1

Ich glaube, dass mit nur einer Abfrage mit einigen verbindet und Summen erreichen können.

Wenn Sie das Schema für Ihre Datenbank angeben, kann ich versuchen, die Antwort zu erarbeiten, für jetzt werde ich Folgendes übernehmen.

create table user (
    id integer primary key, 
    active boolean); 

create table product (
    id integer primary key, 
    user integer non null, 
    price integer non null, 
    foreign key(user) references user(id)); 

create table product_buyer (
    id integer primary key, 
    product integer, 
    buyer integer, 
    foreign key(product) references product(id), 
    foreign key(buyer) references buyer(id)); 

create table buyer (
    id integer primary key, 
    active boolean, 
    last_offer integer); 

sollten Sie bekommen, was Sie wollen aus:

select (
    user.id, 
    sum(case when product.price > buyer.last_offer then 1 end) as expensive, 
    sum(case when product.price = buyer.last_offer then 1 end) as same, 
    sum(case when product.price < buyer.last_offer then 1 end) as cheap) 
from 
    user join product on user.id=product.user 
    join product_buyer on product.id=product_buyer.product 
    join buyer on product_buyer.buyer=buyer.id 
where user.active=1 and buyer.active=1 
group by user.id; 

Sie here einen Blick auf django docs für bedingte Ausdrücke für die CASE-Anweisung haben.

Ich hoffe, es hilft.

Bearbeiten: Ich habe versucht, die Abfrage zu Django (ungetestet) mit Ihrem Modell zu übersetzen.

Product.objects.filter(
    user=my_user, 
    active=True, 
    buyers__valid=True, 
    buyers__offers__valid=True 
).annotate(
    max_date=Max("buyers__offers__datetime") 
).filter(
    datetime=F("max_date") 
).annotate(
    expensive=Case(
     When(buyers__a_price__gt=F("buyers__offers__eur_price"), 
      then=Value(1)) 
    ), 
    same=Case(
     When(buyers__a_price=F("buyers__offers__eur_price"), 
      then=Value(1)) 
    ), 
    cheap=Case(
     When(buyers__a_price__lt=F("buyers__offers__eur_price"), 
      then=Value(1)) 
    ) 
).annotate(
    n_expensive=Sum("expensive"), 
    n_same=Sum("same"), 
    n_cheap=Sum("cheap") 
).values("user", "n_expensive", "n_same", "n_cheap") 

Ich bin nicht sicher, ob es ein Weg, um es in einer prägnanten Art und Weise zu schreiben ist, ist dies die am weitesten ich ohne tatsächlich macht einen django Test-App, check it out gehen würde. Ich überlasse die Verfeinerung Ihnen, da Sie schließlich das Testmodell haben, aber angesichts der obigen SQL sollte der Übersetzungsprozess nur eine Frage der Durcharbeitung der django-Dokumente sein.

+0

Danke. Mein Schema sieht ein bisschen anders aus. Ich habe ein sehr vereinfachtes Modell von models.py zum Ende der Frage hinzugefügt. Ich habe kein "last_offer" -Attribut in der Käufertabelle. Ich berechne es wie buyer.offers.latest ('datetime'). –

+0

Hinzugefügt eine Django-Übersetzung der Abfrage, ich habe es nicht getestet, so dass es sicherlich irgendwo brechen wird: D Aber sein Wert ist mehr, um eine Vorstellung davon zu geben, welche Teile der Django-Dokumente zu betrachten. – qwattash

Verwandte Themen