sql >> Database >  >> RDS >> Mysql

Eenvoudige subquery met OuterRef

Een van de problemen met uw voorbeeld is dat u queryset.count() . niet kunt gebruiken als een subquery, omdat .count() probeert de queryset te evalueren en de telling terug te geven.

Je zou dus kunnen denken dat de juiste aanpak zou zijn om Count() . te gebruiken in plaats van. Misschien zoiets als dit:

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

Dit werkt om twee redenen niet:

  1. De Tag queryset selecteert alle Tag velden, terwijl Count kan maar op één veld rekenen. Dus:Tag.objects.filter(post=OuterRef('pk')).only('pk') is nodig (om te tellen op tag.pk ).

  2. Count zelf is geen Subquery klasse, Count is een Aggregate . Dus de expressie gegenereerd door Count wordt niet herkend als een Subquery (OuterRef subquery vereist), kunnen we dat oplossen door Subquery te gebruiken .

Het toepassen van fixes voor 1) en 2) zou het volgende opleveren:

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

Echter als u de query die wordt geproduceerd inspecteert:

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"

je ziet een GROUP BY clausule. Dit komt omdat COUNT is een verzamelfunctie. Op dit moment heeft het geen invloed op het resultaat, maar in sommige andere gevallen wel. Daarom is de docs stel een andere aanpak voor, waarbij de aggregatie wordt verplaatst naar de subquery via een specifieke combinatie van 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')
    )
)

Uiteindelijk zal dit het volgende opleveren:

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. pgpredict – Voorspellende analyses in PostgreSQL

  2. Resultaten opnemen die gelijk zijn voor de laatste plaats bij gebruik van de TOP-clausule in SQL Server

  3. Microsoft Access-gegevens gebruiken in Wolfram Mathematica

  4. Wat is beter voor uw big data-applicatie, SQL of NoSQL?