Mysql
 sql >> база данни >  >> RDS >> Mysql

Проста подзаявка с OuterRef

Един от проблемите с вашия пример е, че не можете да използвате queryset.count() като подзаявка, защото .count() се опитва да оцени набора от заявки и да върне броя.

Така че някой може да си помисли, че правилният подход би бил да се използва Count() вместо. Може би нещо подобно:

Post.objects.annotate(
    count=Count(Tag.objects.filter(post=OuterRef('pk')))
)

Това няма да работи по две причини:

  1. Tag queryset избира всички Tag полета, докато Count може да разчита само на едно поле. По този начин:Tag.objects.filter(post=OuterRef('pk')).only('pk') е необходимо (за да изберете отчитане на tag.pk ).

  2. Count сама по себе си не е Subquery клас, Count е Aggregate . Така че изразът, генериран от Count не се разпознава като Subquery (OuterRef изисква подзаявка), можем да поправим това, като използваме Subquery .

Прилагането на корекции за 1) и 2) ще доведе до:

Post.objects.annotate(
    count=Count(Subquery(Tag.objects.filter(post=OuterRef('pk')).only('pk')))
)

Въпреки това ако проверите произведената заявка:

SELECT 
    "tests_post"."id",
    "tests_post"."title",
    COUNT((SELECT U0."id" 
            FROM "tests_tag" U0 
            INNER JOIN "tests_post_tags" U1 ON (U0."id" = U1."tag_id") 
            WHERE U1."post_id" = ("tests_post"."id"))
    ) AS "count" 
FROM "tests_post" 
GROUP BY 
    "tests_post"."id",
    "tests_post"."title"

ще забележите GROUP BY клауза. Това е така, защото COUNT е агрегатна функция. В момента това не влияе на резултата, но в някои други случаи може. Ето защо документите предлагат различен подход, при който агрегирането се премества в subquery чрез специфична комбинация от values + annotate + values :

Post.objects.annotate(
    count=Subquery(
        Tag.objects
            .filter(post=OuterRef('pk'))
            # The first .values call defines our GROUP BY clause
            # Its important to have a filtration on every field defined here
            # Otherwise you will have more than one group per row!!!
            # This will lead to subqueries to return more than one row!
            # But they are not allowed to do that!
            # In our example we group only by post
            # and we filter by post via OuterRef
            .values('post')
            # Here we say: count how many rows we have per group 
            .annotate(count=Count('pk'))
            # Here we say: return only the count
            .values('count')
    )
)

Накрая това ще произведе:

SELECT 
    "tests_post"."id",
    "tests_post"."title",
    (SELECT COUNT(U0."id") AS "count" 
            FROM "tests_tag" U0 
            INNER JOIN "tests_post_tags" U1 ON (U0."id" = U1."tag_id") 
            WHERE U1."post_id" = ("tests_post"."id") 
            GROUP BY U1."post_id"
    ) AS "count" 
FROM "tests_post"


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Защо MySQL позволява групиране по заявки БЕЗ агрегатни функции?

  2. SQL Показване на последния запис в GROUP BY?

  3. Намиране на предстоящи рождени дни с jOOQ

  4. Каква е разликата между SERIAL и AUTO_INCREMENT в mysql

  5. SQL:Вземете продукти от категория, но също така трябва да са в друг набор от категории