sql >> Databasteknik >  >> RDS >> Mysql

Enkel underfråga med OuterRef

Ett av problemen med ditt exempel är att du inte kan använda queryset.count() som en underfråga, eftersom .count() försöker utvärdera frågeuppsättningen och returnera räkningen.

Så man kan tro att det rätta tillvägagångssättet skulle vara att använda Count() istället. Kanske något sånt här:

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

Detta kommer inte att fungera av två anledningar:

  1. Tag queryset väljer alla Tag fält, medan Count kan bara räkna med ett fält. Således:Tag.objects.filter(post=OuterRef('pk')).only('pk') behövs (för att välja att räkna på tag.pk ).

  2. Count i sig är inte en Subquery klass, Count är ett Aggregate . Alltså uttrycket som genereras av Count känns inte igen som en Subquery (OuterRef kräver underfråga), kan vi fixa det genom att använda Subquery .

Att tillämpa korrigeringar för 1) och 2) skulle producera:

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

Men om du inspekterar frågan som produceras:

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"

du kommer att märka en GROUP BY klausul. Detta beror på att COUNT är en aggregerad funktion. Just nu påverkar det inte resultatet, men i vissa andra fall kan det. Det är därför som dokumenten föreslå ett annat tillvägagångssätt, där aggregeringen flyttas till subquery via en specifik kombination av 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')
    )
)

Slutligen kommer detta att producera:

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. Hur kan jag få en lista över alla funktioner lagrade i databasen för ett visst schema i PostgreSQL?

  2. obuffrad fråga med MySQLi?

  3. Hur jämför man programversioner med SQL Server?

  4. Skicka flera uppsättningar eller matriser av värden till en funktion