2017-03-01 47 views
0

功能的一部分是計算最後一個有效報價的數量,它們低於,等於和高於a_price。無法找出更好的方法,因此我現在使用兩個循環。問題是這會執行575個太多的查詢。如何減少嵌套循環中的查詢數(575)?

一個產品可以有很多買家和買家有很多優惠(具有不同的日期)。我試圖添加prefetch_related('buyers')但它根本沒有幫助。

編輯:在這種情況下,有32個產品,每個產品有0到30個買家。

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) 

你知道我應該怎麼做才能減少查詢次數?

編輯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(...) 

回答

1

我相信你可以做到只要再加一些聯接和款項查詢。

如果您爲數據庫提供模式,我可以嘗試詳細說明答案,現在我將假設以下內容。

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); 

你應該得到你想要的東西從:

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; 

你可以看看Django文檔爲CASE語句here條件表達式。

希望它有幫助。

編輯: 我試圖將查詢翻譯成django(未經測試)與您的模型。

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") 

我不知道是否有把它寫在一個更簡潔的方式的方式,這是最遠的,我會去沒有實際進行Django的測試應用程序檢查出來。 由於您最終擁有測試模型,所以我會對您進行細化,但是考慮到翻譯過程中的SQL應該只是通過django文檔的問題。

+0

謝謝。我的模式看起來有點不同。我已經從models.py中添加了一個非常簡化的模型,直到問題的底部。買方表中沒有'last_offer'屬性。我計算它像buyer.offers.latest('datetime')。 –

+0

添加了查詢的django翻譯,我沒有對它進行測試,所以它肯定會在某個地方出現:D但它的價值更多是爲了瞭解django文檔的哪些部分。 – qwattash