Mysql
 sql >> Teknologi Basis Data >  >> RDS >> Mysql

Subquery Sederhana dengan OuterRef

Salah satu masalah dengan contoh Anda adalah Anda tidak dapat menggunakan queryset.count() sebagai subquery, karena .count() mencoba mengevaluasi kumpulan kueri dan mengembalikan hitungan.

Jadi orang mungkin berpikir bahwa pendekatan yang tepat adalah menggunakan Count() sebagai gantinya. Mungkin seperti ini:

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

Ini tidak akan berhasil karena dua alasan:

  1. Tag queryset memilih semua Tag bidang, sementara Count hanya bisa mengandalkan satu bidang. Jadi:Tag.objects.filter(post=OuterRef('pk')).only('pk') diperlukan (untuk memilih penghitungan pada tag.pk ).

  2. Count itu sendiri bukan Subquery kelas, Count adalah Aggregate . Jadi ekspresi yang dihasilkan oleh Count tidak dikenali sebagai Subquery (OuterRef membutuhkan subquery), kita dapat memperbaikinya dengan menggunakan Subquery .

Menerapkan perbaikan untuk 1) dan 2) akan menghasilkan:

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

Namun jika Anda memeriksa kueri yang dihasilkan:

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"

Anda akan melihat GROUP BY ayat. Ini karena COUNT adalah fungsi agregat. Saat ini tidak mempengaruhi hasil, tetapi dalam beberapa kasus lain mungkin. Itulah mengapa dokumen menyarankan pendekatan yang berbeda, di mana agregasi dipindahkan ke subquery melalui kombinasi spesifik 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')
    )
)

Akhirnya ini akan menghasilkan:

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. Kata sandi yang ditentukan untuk akun pengguna 'root' tidak valid, atau gagal terhubung ke server basis data

  2. Saya mencoba kueri sederhana dengan parameter in dengan mysql 5.5 melalui kode jdbc

  3. Indeks terakhir dari substring yang diberikan di MySQL

  4. Sisa di PostgreSQL, MS SQL Server, MySQL, dan SQLite

  5. PILIH banyak ke banyak tabel dalam satu kueri