Django Postgresql ORM оптимизация

У меня есть представление PostgreSQL под названием view_sales_dashboard - оно состоит из нескольких миллионов строк ежедневных данных о продажах.

В представлении Django я хочу представить таблицу, сгруппированную по продуктам, со столбцами в виде итогов base_daily_pnl за различные периоды времени - Ежедневно, Месяц до даты (MTD), Квартал до даты (QTD), Год до даты (YTD) и Начало до даты (ITD)

Для того чтобы попытаться ограничить количество SQL-запросов, я создаю 5 наборов запросов, чтобы затем сгенерировать таблицу. Чтобы повысить эффективность этого, я изучил журналы и ожидал увидеть 5 SQL-запросов. Однако журнал показывает 20 запросов (5 типов товаров * 4 агрегатные группировки + запрос ежедневной серии).

Смотрите ниже код Django, модель ORM и журналы.

Может ли кто-нибудь подсказать 1.) почему выполняется так много SQL-запросов 2.) как оптимизировать?

queryset_sales_all = SalesDashboard.objects.all()

queryset_daily_products = queryset_pnl_all.filter(position_date__range=[latest_pnl_date_str, latest_pnl_date_str]).values('product').annotate(base_daily_pnl=Sum('base_daily_pnl'),base_lmv=Sum('base_lmv'))  
    
    for daily in queryset_daily_product:
        matching_mtd = queryset_pnl_all.filter(position_date__range=[start_mth_str,latest_pnl_date_str]).values('product').annotate(mtd_pnl=Sum('base_daily_pnl')).get(product=daily['product'])
        matching_qtd = queryset_pnl_all.filter(position_date__range=[start_qtd_str, latest_pnl_date_str]).values('product').annotate(qtd_pnl=Sum('base_daily_pnl')).get(product=daily['product'])
        matching_ytd = queryset_pnl_all.filter(position_date__range=[start_year_str, latest_pnl_date_str]).values('product').annotate(ytd_pnl=Sum('base_daily_pnl')).get(product=daily['product'])
        matching_itd = queryset_pnl_all.filter(position_date__range=[start_itd_str, latest_pnl_date_str]).values('product').annotate(itd_pnl=Sum('base_daily_pnl')).get(product=daily['product'])
        daily['mtd_pnl'] = matching_mtd['mtd_pnl']
        daily['qtd_pnl'] = matching_qtd['qtd_pnl']
        daily['ytd_pnl'] = matching_ytd['ytd_pnl']
        daily['itd_pnl'] = matching_itd['itd_pnl'] 

pnl_product = SummaryPnlProductTable(queryset_daily_product)   

Ниже представлена модель ORM:

class SalesDashboard(models.Model):    

    unqiue_id = models.IntegerField(primary_key=True)    
    sales_id = models.CharField(max_length=50)
    base_daily_pnl = models.FloatField(default=0)
    position_date = models.DateField()
    book_id = models.IntegerField()
    book = models.CharField(max_length=100, blank=True, null=True)
    product = models.CharField(max_length=100, blank=True, null=True)
    customer = models.CharField(max_length=100)
    base_lmv = models.FloatField(default=0)

   
    class Meta: 
        managed = False       
        db_table = 'view_sales_dashboard

Запись в журнал:

Вы можете легко "сгруппировать" запросы так, чтобы нам понадобилось только четыре для всех продуктов.

Сначала нам нужно сделать словари более доступными, мы можем сделать это с помощью:

lut = {item['products']: item for item in queryset_daily_products}


def update_lut(qs, start, target, lut):
    for item in (
        qs.filter(
            position_date__range=[start, latest_pnl_date_str], product__in=lut
        )
        .values('product')
        .annotate(result=Sum('base_daily_pnl'))
    ):
        lut[item['product']][target] = item['result']


update_lut(queryset_pnl_all, start_mth_str, 'mtd_pnl', lut)
update_lut(queryset_pnl_all, start_qtd_str, 'qtd_pnl', lut)
update_lut(queryset_pnl_all, start_year_str, 'ytd_pnl', lut)
update_lut(queryset_pnl_all, start_itd_str, 'itd_pnl', lut)

На самом деле мы можем переписать это, чтобы сделать это с помощью одного запроса, используя filter=… параметр [Django-doc], но это, скорее всего, не нужно.

Вернуться на верх