我有以下 SQL 查询正在运行:
SELECT
mrc.token_id,
ARRAY_AGG(mt.name) AS tracking
FROM
markets_rankscurrent mrc
LEFT JOIN (
SELECT
mtg.id,
mtg.index_id,
mtg.favorites_group_id,
mtg.name,
COALESCE(ufe.token_id, mte.token_id, mie.token_id) AS token_id
FROM
markets_trackinggroup mtg
LEFT JOIN users_favoritesentry ufe ON mtg.favorites_group_id = ufe.group_id
LEFT JOIN markets_trackingentry mte ON mtg.id = mte.group_id
LEFT JOIN markets_indexentry mie ON mtg.index_id = mie.index_id
) mt ON mrc.token_id = mt.token_id
GROUP BY
mrc.token_id;
这是我的模型:
class Token(models.Model):
class RanksCurrent(models.Model):
token = models.ForeignKey(Token, on_delete=models.CASCADE, db_index=False)
class TrackingGroup(models.Model):
name = models.CharField(max_length=60, verbose_name='Name')
favorites_group = models.ForeignKey(FavoritesGroup, on_delete=models.CASCADE, related_name='tracking_groups', blank=True, null=True)
index = models.ForeignKey(Token, on_delete=models.CASCADE, related_name='tracking_groups', blank=True, null=True)
class TrackingEntry(models.Model):
group = models.ForeignKey(TrackingGroup, on_delete=models.CASCADE, related_name='tokens')
token = models.ForeignKey(Token, on_delete=models.CASCADE, related_name='tracking_entries')
class IndexEntry(models.Model):
index = models.ForeignKey(Token, on_delete=models.CASCADE, related_name='index_tokens')
token = models.ForeignKey(Token, on_delete=models.CASCADE, related_name='indices')
class FavoritesGroup(models.Model):
pass
class FavoritesEntry(models.Model):
group = models.ForeignKey(FavoritesGroup, on_delete=models.CASCADE, related_name='favorites_entries')
token = models.ForeignKey('markets.Token', on_delete=models.CASCADE, related_name='favorites_entries')
TrackingGroup.index
外键只会设置为 Token
对象,该对象也是 IndexEntry
表中的外键。
我的最终目标是能够查询
RanksCurrent
表并注释包含 tracking_groups
名称列表的 TrackingGroup
列,其中 Token
是成员。我尝试使用 Subquery
和 ArrayAgg
来尝试执行此操作,但是如果我的子查询返回一个列表(就像我想要的那样),它将失败。
这些类型的方法可用于获取 TrackingGroup 名称列表:
tracking_subquery = TrackingGroup.objects.filter(
Q(index__index_tokens__token=OuterRef('token_id')) |
Q(favorites_group__favorites_entries__token=OuterRef('token_id')) |
Q(tokens__token=OuterRef('token_id'))
).values('name')
tracking_subquery = TrackingGroup.objects.filter(
Q(favorites_group_id__in=FavoritesEntry.objects.filter(token_id=OuterRef(OuterRef('token_id'))).values('group_id')) |
Q(id__in=TrackingEntry.objects.filter(token_id=OuterRef(OuterRef('token_id'))).values('group_id')) |
Q(index_id__in=IndexEntry.objects.filter(token_id=OuterRef(OuterRef('token_id'))).values('index_id'))
).values('name')
但是,当我尝试注释主查询时,它失败了:
RanksCurrent.objects.annotate(
tracking=ArrayAgg(Subquery(tracking_subquery))
)
我以为我需要将
ArrayAgg
调用移到子查询内,但这并没有什么区别:
RanksCurrent.objects.annotate(
tracking=Subquery(
TrackingGroup.objects.filter(
Q(index__index_tokens__token=OuterRef('token_id')) |
Q(favorites_group__favorites_entries__token=OuterRef('token_id')) |
Q(tokens__token=OuterRef('token_id'))
).values('name').annotate(group_names=ArrayAgg('name')).values('group_names')))
据我所知,SQL 起作用的原因是因为内部
SELECT
语句为每个 token_id
返回唯一的行,而不是为每个 TrackingGroup
返回一行。
我现在的想法是将其分解为三个单独的查询,每个查询都针对一个 *Entry 表,然后以某种方式将结果列表合并为一个列表并用它注释查询集,或者只是创建三个单独的注释。如果不是为了让该操作在 SQL 中工作,我早就这样做了,所以现在我想在完全放弃之前尝试在 Django 中做同样的事情。
我当前的解决方案是执行三个单独的查询并将它们合并到序列化器中:
tracking_index_groups_subquery = Subquery(
queryset_filtered.filter(
token__indices__index__tracking_groups__isnull=False,
id=OuterRef('id')
).values('id').annotate(
index_tracking=ArrayAgg('token__indices__index__tracking_groups__name')
).values('index_tracking')[:1]
)
tracking_favorites_groups_subquery = Subquery(
queryset_filtered.filter(
token__favorites_entries__group__tracking_groups__isnull=False,
id=OuterRef('id')
).values('id').annotate(
favorites_tracking=ArrayAgg('token__favorites_entries__group__tracking_groups__name')
).values('favorites_tracking')[:1]
)
tracking_groups_subquery = Subquery(
queryset_filtered.filter(
token__tracking_entries__group__isnull=False,
id=OuterRef('id')
).values('id').annotate(
tracking=ArrayAgg('token__tracking_entries__group__name')
).values('tracking')[:1]
)
queryset_filtered = queryset_filtered.annotate(
index_tracking=tracking_index_groups_subquery,
favorites_tracking=tracking_favorites_groups_subquery,
tracking=tracking_groups_subquery
)
class RanksCurrentSerializer(serializers.ModelSerializer):
tracking = serializers.SerializerMethodField('get_tracking')
def get_tracking(self, obj):
tracking = []
if obj.index_tracking:
tracking.extend(obj.index_tracking)
if obj.favorites_tracking:
tracking.extend(obj.favorites_tracking)
if obj.tracking:
tracking.extend(obj.tracking)
return tracking