Mysql
 sql >> Baza danych >  >> RDS >> Mysql

Proste podzapytanie z OuterRef

Jednym z problemów z twoim przykładem jest to, że nie możesz użyć queryset.count() jako podzapytanie, ponieważ .count() próbuje ocenić zestaw zapytań i zwrócić liczbę.

Można więc pomyśleć, że właściwym podejściem byłoby użycie Count() zamiast. Może coś takiego:

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

To nie zadziała z dwóch powodów:

  1. Tag queryset zaznacza wszystkie Tag pola, podczas gdy Count może liczyć tylko na jedno pole. Tak więc:Tag.objects.filter(post=OuterRef('pk')).only('pk') jest potrzebne (aby wybrać liczenie na tag.pk ).

  2. Count samo w sobie nie jest Subquery klasa, Count jest Aggregate . Zatem wyrażenie wygenerowane przez Count nie jest rozpoznawany jako Subquery (OuterRef wymaga podzapytania), możemy to naprawić za pomocą Subquery .

Zastosowanie poprawek dla 1) i 2) spowodowałoby:

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

Jednak jeśli sprawdzisz tworzone zapytanie:

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"

zauważysz GROUP BY klauzula. Dzieje się tak, ponieważ COUNT jest funkcją agregującą. W tej chwili nie ma to wpływu na wynik, ale w niektórych innych przypadkach może. Dlatego dokumenty zaproponuj inne podejście, w którym agregacja jest przenoszona do subquery za pomocą określonej kombinacji 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')
    )
)

W końcu spowoduje to:

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. EF6 z ​​MySQL. Podany klucz nie był obecny w słowniku

  2. PHP, MySQL, PDO — uzyskać wynik z zapytania UPDATE?

  3. Jak przyznać użytkownikowi uprawnienia dostępu zdalnego do serwera mysql?

  4. jeśli nie istnieje, wstaw do MySql

  5. Dwie kolumny jako klucze podstawowe w mysql?