从 Django 查询中删除多余的 INNER JOIN
Removing a superfluous INNER JOIN from a Django query
我有这样的模型来描述音乐专辑、其中的曲目以及个人收听特定曲目:
class Album(models.Model):
name = models.CharField(max_length=255)
class Track(models.Model):
name = models.CharField(max_length=255)
class Listen(models.Model):
track = models.ForeignKey('Track', related_name='listens', db_index=True)
album = models.ForeignKey('Album', related_name='listens', db_index=True, blank=True)
要获取一张专辑中的所有曲目,按它们被听到的次数排序,我可以这样做:
Track.objects \
.annotate( listen_count=models.Count('listens', distinct=True) ) \
.filter(listens__album=1294) \
.order_by('-listen_count')
这得到了正确的结果,但似乎效率不高。结果查询的简化版本是:
SELECT track.id,
track.name,
COUNT(DISTINCT listen.id) AS listen_count
FROM track
LEFT OUTER JOIN listen ON (track.id = listen.track_id)
INNER JOIN listen T3 ON (track.id = T3.track_id)
WHERE T3.album_id = 1294
GROUP BY track.id, track.name
ORDER BY listen_count DESC
我可以通过失去 INNER JOIN
:
得到相同的结果
SELECT track.id,
track.name,
COUNT(DISTINCT listen.id) AS listen_count
FROM track
LEFT OUTER JOIN listen ON (track.id = listen.track_id)
WHERE listen.album_id = 1294
GROUP BY track.id, track.name
ORDER BY listen_count DESC
少用一个索引,速度大约减半。但我无法弄清楚如何让 Django ORM 执行此操作。 (我目前正在使用 SQLite,如果这有所不同,尽管稍后会使用 Postgresql。)
如果你.filter
拳头和.annotate
之后,你的JOIN将被重用
>>> qs = (Track.objects
... .filter(listens__album=1294)
... .annotate(listen_count=models.Count('listens', distinct=True))
... .order_by('-listen_count')
... )
将导致
SELECT
"music_track"."id",
"music_track"."name",
COUNT(DISTINCT "music_listen"."id") AS "listen_count"
FROM "music_track"
INNER JOIN "music_listen" ON ("music_track"."id" = "music_listen"."track_id")
WHERE "music_listen"."album_id" = 1294
GROUP BY "music_track"."id", "music_track"."name"
ORDER BY "listen_count" DESC
我有这样的模型来描述音乐专辑、其中的曲目以及个人收听特定曲目:
class Album(models.Model):
name = models.CharField(max_length=255)
class Track(models.Model):
name = models.CharField(max_length=255)
class Listen(models.Model):
track = models.ForeignKey('Track', related_name='listens', db_index=True)
album = models.ForeignKey('Album', related_name='listens', db_index=True, blank=True)
要获取一张专辑中的所有曲目,按它们被听到的次数排序,我可以这样做:
Track.objects \
.annotate( listen_count=models.Count('listens', distinct=True) ) \
.filter(listens__album=1294) \
.order_by('-listen_count')
这得到了正确的结果,但似乎效率不高。结果查询的简化版本是:
SELECT track.id,
track.name,
COUNT(DISTINCT listen.id) AS listen_count
FROM track
LEFT OUTER JOIN listen ON (track.id = listen.track_id)
INNER JOIN listen T3 ON (track.id = T3.track_id)
WHERE T3.album_id = 1294
GROUP BY track.id, track.name
ORDER BY listen_count DESC
我可以通过失去 INNER JOIN
:
SELECT track.id,
track.name,
COUNT(DISTINCT listen.id) AS listen_count
FROM track
LEFT OUTER JOIN listen ON (track.id = listen.track_id)
WHERE listen.album_id = 1294
GROUP BY track.id, track.name
ORDER BY listen_count DESC
少用一个索引,速度大约减半。但我无法弄清楚如何让 Django ORM 执行此操作。 (我目前正在使用 SQLite,如果这有所不同,尽管稍后会使用 Postgresql。)
如果你.filter
拳头和.annotate
之后,你的JOIN将被重用
>>> qs = (Track.objects
... .filter(listens__album=1294)
... .annotate(listen_count=models.Count('listens', distinct=True))
... .order_by('-listen_count')
... )
将导致
SELECT
"music_track"."id",
"music_track"."name",
COUNT(DISTINCT "music_listen"."id") AS "listen_count"
FROM "music_track"
INNER JOIN "music_listen" ON ("music_track"."id" = "music_listen"."track_id")
WHERE "music_listen"."album_id" = 1294
GROUP BY "music_track"."id", "music_track"."name"
ORDER BY "listen_count" DESC