Optimise two SQL queries and a list comprehension

我的未来我决定 提交于 2020-01-24 21:06:46

问题


I have those two models (simplified):

class Place(OrderedModel):
    name = models.CharField(max_length=100)

class Team(models.Model):
    name = models.CharField(max_length=100)
    places = models.ManyToManyField(Place, blank=True)

Say that there are ten instances of Place. But only five instances of Team. I want to return a list of booleans in the order of entries in Place, where True means that Team.places contains that place and False (obviously) means that it doesn't. The Team.places field has no particular order.

In the following example the Team have the instances of the first four Place objects and the last one in it's places field:

[True, True, True, True, False, False, False, False, False, True]

I solved this with this method on the Team model:

class Team(models.Model):
    ...
    def done(self):
        """
        Returns a list of Booleans, one for each place
        in order. True means that the team has found the
        place. False, that they have not.
        """
        places = Place.objects.all()
        return [p in self.places.all() for p in places]

It works but seems highly inefficient. It make (I believe) two different SQL queries and then a list comprehension. Is there a more efficient way to solve this?

Solved

I ended up doing this:

def done(self):
    """
    Returns a list of Booleans, one for each place
    in order. True means that the team has found the
    place. False, that they have not.
    """
    sql = '''SELECT P.id, (TP.id IS NOT NULL) AS done
    FROM qrgame_place P
    LEFT OUTER JOIN qrgame_team_places TP
    ON P.id = TP.place_id AND TP.team_id = %s'''
    places = Place.objects.raw(sql, [self.id])
    for p in places:
        yield bool(p.done)

回答1:


You need this SQL query:

SELECT P.id, (TP.id IS NOT NULL) AS done
FROM myapp_place P
LEFT OUTER JOIN myapp_team_places TP
ON P.id = TP.place_id AND TP.team_id = %s

(You'll also need to add an ORDER BY clause to return the Place objects in the order you want, but since I can't see that part of your model, I can't tell what that clause ought to look like.)

I don't know how to express this query using Django's object-relational mapping system, but you can always run it using a raw SQL query:

>>> sql = ''' ... as above ... '''
>>> places = Place.objects.raw(sql, [team.id])
>>> for p in places:
...     print p.id, bool(p.done)
...
1 True
2 True
3 False



回答2:


A more optimal solution would be:

def done(self):    
    places = Place.objects.values_list('pk', flat=True)
    team_places = self.places.values_list('pk', flat=True)

    return [place in team_places for place in places]


来源:https://stackoverflow.com/questions/15899701/optimise-two-sql-queries-and-a-list-comprehension

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!