sql >> Database teknologi >  >> RDS >> Mysql

Simpel underforespørgsel med OuterRef

Et af problemerne med dit eksempel er, at du ikke kan bruge queryset.count() som en underforespørgsel, fordi .count() forsøger at evaluere forespørgselssættet og returnere antallet.

Så man kan tro, at den rigtige tilgang ville være at bruge Count() i stedet. Måske noget som dette:

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

Dette virker ikke af to grunde:

  1. Tag queryset vælger alle Tag felter, mens Count kan kun regne med ét felt. Således:Tag.objects.filter(post=OuterRef('pk')).only('pk') er nødvendig (for at vælge tæller på tag.pk ).

  2. Count i sig selv er ikke en Subquery klasse, Count er en Aggregate . Altså udtrykket genereret af Count genkendes ikke som en Subquery (OuterRef kræver underforespørgsel), kan vi rette det ved at bruge Subquery .

Anvendelse af rettelser til 1) og 2) ville producere:

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

Men hvis du inspicerer forespørgslen, der produceres:

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 vil bemærke en GROUP BY klausul. Dette er fordi COUNT er en aggregeret funktion. Lige nu påvirker det ikke resultatet, men i nogle andre tilfælde kan det evt. Det er derfor, dokumenterne foreslå en anden tilgang, hvor aggregeringen flyttes ind i subquery via en specifik kombination af 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')
    )
)

Til sidst vil dette producere:

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. Sådan bruger du 'select' i MySQL 'insert'-sætning

  2. Kan jeg bruge VARCHAR som PRIMÆR NØGLE?

  3. Postgres er den fedeste database – Årsag #1:Udviklere elsker det!

  4. Room Migration Alter Tabel tilføjer ikke ny kolonne og migrerer bliver kaldt igen og igen