#!/usr/bin/env python """ Скрипт для демонстрации N+1 проблемы в текущей реализации """ import os import sys from pathlib import Path import django # Setup Django sys.path.insert(0, str(Path(__file__).parent.parent)) os.environ.setdefault("DJANGO_SETTINGS_MODULE", "eusrr_backend.settings") django.setup() from django.conf import settings from django.contrib.auth import get_user_model from django.db import connection, reset_queries from django.db.models import Count, OuterRef, Q, Subquery from django.db.models.functions import Coalesce from communications.models import Chat, ChatReadState, Message # Включаем отслеживание SQL settings.DEBUG = True Employee = get_user_model() def test_current_implementation(): """Текущая реализация с подзапросами""" user = Employee.objects.first() if not user: print("Нет пользователей в базе") return print(f"Тестируем для пользователя: {user}") print("=" * 80) # Сбрасываем счетчик запросов reset_queries() # Текущая реализация из ChatViewSet.get_queryset() read_state_subq = ChatReadState.objects.filter( chat=OuterRef("pk"), user=user ).values("last_read_message_id")[:1] unread_count_subq = ( Message.objects.filter( chat=OuterRef("pk"), id__gt=Coalesce(Subquery(read_state_subq), 0), is_deleted=False, ) .exclude(author=user) .values("chat") .annotate(count=Count("id")) .values("count") ) chats = ( Chat.objects.filter( Q(participants=user) | Q( department__in=user.departments_links.filter(is_active=True).values( "department" ) ) | Q(include_all_employees=True) ) .annotate(unread_count=unread_count_subq) .distinct()[:10] ) # Берем первые 10 чатов # Принудительно выполняем запрос chat_list = list(chats) print(f"\nЗагружено чатов: {len(chat_list)}") print(f"Количество SQL запросов: {len(connection.queries)}") print("\n" + "=" * 80) # Показываем первые 3 запроса for i, query in enumerate(connection.queries[:3], 1): print(f"\nЗапрос #{i}:") print(query["sql"][:500]) if len(query["sql"]) > 500: print("... (обрезано)") print(f"Время: {query['time']}s") if len(connection.queries) > 3: print(f"\n... еще {len(connection.queries) - 3} запросов") print("\n" + "=" * 80) print("\nПРОБЛЕМА:") print("Несмотря на то, что это ОДИН SQL запрос, внутри него есть") print("ПОДЗАПРОСЫ (subqueries) для каждого чата в SELECT clause.") print("PostgreSQL выполняет их для КАЖДОЙ строки результата.") print("\nЭто не классический N+1, но похожая проблема производительности.") return chat_list if __name__ == "__main__": test_current_implementation()