sql >> Databáze >  >> RDS >> Mysql

Jednoduchý poddotaz s OuterRef

Jedním z problémů vašeho příkladu je, že nemůžete použít queryset.count() jako poddotaz, protože .count() se pokusí vyhodnotit sadu dotazů a vrátit počet.

Někdo si tedy může myslet, že správným přístupem by bylo použití Count() namísto. Možná něco takového:

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

Toto nebude fungovat ze dvou důvodů:

  1. Tag queryset vybere všechny Tag pole, zatímco Count může počítat pouze s jedním polem. Tedy:Tag.objects.filter(post=OuterRef('pk')).only('pk') je potřeba (pro výběr počítání na tag.pk ).

  2. Count sám o sobě není Subquery třída, Count je Aggregate . Tedy výraz generovaný Count není rozpoznáno jako Subquery (OuterRef vyžaduje poddotaz), můžeme to opravit pomocí Subquery .

Použití oprav pro 1) a 2) by přineslo:

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

Nicméně pokud zkontrolujete vytvářený dotaz:

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"

všimnete si GROUP BY doložka. Důvodem je COUNT je agregační funkce. Momentálně to neovlivňuje výsledek, ale v některých jiných případech může. Proto dokumenty navrhnout jiný přístup, kdy se agregace přesune do subquery prostřednictvím specifické kombinace 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')
    )
)

Nakonec to vytvoří:

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. Jak automaticky zvýšit o 2 pro konkrétní tabulku v mysql

  2. RuntimeError:OperationalError:(2003, nelze se připojit k serveru MySQL na 'IPaddress of the instance'

  3. Příkaz InnoDB SELECT ... FOR UPDATE zamykání všech řádků v tabulce

  4. výkon mysql count