2017-03-01 39 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