Django lte / gte查询列表

时间:2013-06-11 18:54:59

标签: python django performance list filter

我有以下类型的数据:

数据被分段为“帧”,每个帧都有一个开始和停止“gpstime”。在每个帧内都有一堆带有“gpstime”值的点。

有一个帧模型有frame_name,start_gps,stop_gps,...

假设我有一个gpstime值列表,并希望为每个值找到相应的frame_name。

我可以做一个循环...

framenames = [frames.objects.filter(start_gps__lte=gpstime[idx],stop_gps__gte=gpstime[idx]).values_list('frame_name',flat=True) for idx in range(len(gpstime))]

这将给我一个'frame_name'列表,每个gpstime一个。这就是我要的。但这很慢。

我想知道的是:是否有更好的方法来预先形成此查找以获取每个gpstime的帧名称,这比迭代列表更有效。这份清单可能会变得非常大。

谢谢!

编辑:框架型号

class frames(models.Model):
    frame_id = models.AutoField(primary_key=True)
    frame_name = models.CharField(max_length=20)
    start_gps = models.FloatField()
    stop_gps = models.FloatField()

    def __unicode__(self):
        return "%s"%(self.frame_name)

3 个答案:

答案 0 :(得分:1)

如果我理解正确,gpstime是一个时间列表,你想要为每个gpstime生成一个帧名列表。您当前的这种做法确实非常慢,因为它为每个时间戳进行数据库查询。您需要最小化db命中数。

首先出现在我头上的答案使用了numpy。请注意,我在这里没有做任何额外的假设。如果您的gpstime列表可以排序,即排序无关紧要,那么可以更快地完成。

尝试这样的事情:

from numpy import array
frame_start_times=array(Frame.objects.all().values_list('start_time'))
frame_end_times=array(Frame.objects.all().values_list('end_time'))
frame_names=array(Frame.objects.all().values_list('frame_name'))
frame_names_for_times=[]
for time in gpstime:
    frame_inds=frame_start_times[(frame_start_times<time) & (frame_end_times>time)]
    frame_names_for_times.append(frame_names[frame_inds].tostring())

编辑: 由于列表已排序,您可以使用.searchsorted()

from numpy import array as a
gpstimes=a([151,152,153,190,649,652,920,996])
starts=a([100,600,900,1000])
ends=a([180,650,950,1000])
names=a(['a','b','c','d',])
names_for_times=[]

for time in gpstimes:
    start_pos=starts.searchsorted(time)
    end_pos=ends.searchsorted(time)
    if start_pos-1 == end_pos:
        print time, names[end_pos]
    else:
        print str(time) + ' was not within any frame'

答案 1 :(得分:0)

加快速度的最佳方法是为这些字段添加索引:

start_gps = models.FloatField(db_index=True)
stop_gps = models.FloatField(db_index=True)

然后运行manage.py dbsync

答案 2 :(得分:0)

  

帧表非常大,但我有另一个降低的值   在这种情况下搜索到的帧数低于50.实际上没有   模式,每帧从前一次停止的同一个gpstime开始。

我不太明白你是如何将搜索到的帧数减少到50的,但是如果你只在50 gpstime中搜索10,000 frames个值,那么它可能是最简单的将这50帧加载到RAM中,并使用类似于foobarbecue的答案在Python中进行搜索。

但是,如果您在整个表中搜索10 gpstime个值,例如10,000,000 frames,那么您可能不希望将所有10,000,000个帧加载到RAM中。

您可以通过添加以下索引来让DB执行类似操作...

ALTER TABLE myapp_frames ADD UNIQUE KEY my_key (start_gps, stop_gps, frame_name);

...然后使用像这样的查询...

(SELECT frame_name FROM myapp_frames
        WHERE 2.5 BETWEEN start_gps AND stop_gps LIMIT 1)
    UNION ALL
(SELECT frame_name FROM myapp_frames
        WHERE 4.5 BETWEEN start_gps AND stop_gps LIMIT 1) 
    UNION ALL
(SELECT frame_name FROM myapp_frames
        WHERE 7.5 BETWEEN start_gps AND stop_gps LIMIT 1);

...返回......

+------------+
| frame_name |
+------------+
| Frame 2    |
| Frame 4    |
| Frame 7    |
+------------+

... EXPLAIN显示......

+----+--------------+--------------+-------+---------------+--------+---------+------+------+--------------------------+
| id | select_type  | table        | type  | possible_keys | key    | key_len | ref  | rows | Extra                    |
+----+--------------+--------------+-------+---------------+--------+---------+------+------+--------------------------+
|  1 | PRIMARY      | myapp_frames | range | my_key        | my_key | 8       | NULL |    3 | Using where; Using index |
|  2 | UNION        | myapp_frames | range | my_key        | my_key | 8       | NULL |    5 | Using where; Using index |
|  3 | UNION        | myapp_frames | range | my_key        | my_key | 8       | NULL |    8 | Using where; Using index |
| NULL | UNION RESULT | <union1,2,3> | ALL   | NULL          | NULL   | NULL    | NULL | NULL |                          |
+----+--------------+--------------+-------+---------------+--------+---------+------+------+--------------------------+

...所以你可以在一个查询中执行所有查找,这些查询会命中该索引,索引应该缓存在RAM中。