2009-12-05 124 views
8

我有一個名爲Valor的模型。 Valor有一個機器人。我是這樣查詢的:快速查找Django QuerySet中的最後一個元素?

Valor.objects.filter(robot=r).reverse()[0] 

得到最後一個Valor機器人。 Valor.objects.filter(robot = r).count()約爲200000,在我的電腦中獲取最後的項目大約需要4秒。

我該如何加快速度?我在問錯方法?

+0

你有一個非常華麗的結構的ForeignKey,OneToOneField或ManyToManyField關係? – 2009-12-05 17:30:59

+0

fwiw,這很慢,因爲你選擇'valor'表中的所有東西,當你將它轉換成一個列表(通過'.reverse')時,爲每個條目實例化一個django模型實例,並且只取得名單。 – Carson 2009-12-06 18:25:26

回答

3

這聽起來像你的數據集將會足夠大,你可能想要稍微規範一些東西。你有沒有試過跟蹤Robot對象中的最後一個Valor對象?

class Robot(models.Model): 
    # ... 
    last_valor = models.ForeignKey('Valor', null=True, blank=True) 

,然後使用post_savesignal做出更新。

from django.db.models.signals import post_save 

def record_last_valor(sender, **kwargs): 
    if kwargs.get('created', False): 
     instance = kwargs.get('instance') 
     instance.robot.last_valor = instance 

post_save.connect(record_last_valor, sender=Valor) 

當您創建勇猛的對象,但last_valor查找將超快您將支付額外的數據庫交易的成本。玩它,看看您的應用程序是否值得。

+1

這是一個很好的解決設計問題的方法,但是它留下了一個問題,即爲什麼他的設計在執行更合適的查詢時表現不佳他原來的一個。通過適當的索引,排序和限制,他原來的標準化設計也應該「快速」。我希望OP能夠用他的發現來更新這個問題。 – 2009-12-05 18:51:33

+0

Django的要求我將其改爲: 高清record_last_valor(發件人,** kwargs): 如果創建的話: instance.robot.last_valor =例如 – 2009-12-05 19:01:51

+0

我同意喬,這並不試圖回答的基本DB的性能問題。似乎已經有相當多的關於檢查索引的討論,並且關於qs.query的建議看起來像是調查檢查索引方法的最佳方式。 – istruble 2009-12-05 20:17:11

3

那麼,沒有order_by子句,所以我想知道你的意思是'最後'。假設你的意思是'最後加入',

Valor.objects.filter(robot=r).order_by('-id')[0] 

可能爲你做這項工作。

+0

在Valor模型中,我有Meta:ordering =('id',)。這是否使您的查詢與我的一樣? – 2009-12-05 17:11:30

+0

我試過了你的解決方案,它比我的速度慢:( – 2009-12-05 17:13:01

+0

你的機器人ID是否在你的勇敢表上有一個合適的索引? – 2009-12-05 17:16:34

0

django中是否有限制條款?這樣你可以擁有數據庫,只需返回一條記錄。

MySQL的

select * from table where x = y limit 1 

SQL服務器

select top 1 * from table where x = y 

甲骨文

select * from table where x = y and rownum = 1 

我知道這是不是翻譯成Django的,但有人可以回來打掃一下。

+0

限制和反向,你的意思是吧? – 2009-12-05 17:15:00

+0

在Django中,用Python的切片sintax。Valor.objects.filter(robot = r).reverse()[0:1] .get() – 2009-12-05 17:18:41

+0

我剛剛嘗試過最後一個查詢,並且速度與原始查詢相同。 – 2009-12-05 17:21:02

7

如果以前的建議都不起作用,我建議將Django排除在等式之外並針對您的數據庫運行此原始sql。我在猜測你的餐桌名稱,所以你可能需要做相應的調整:

SELECT * FROM valor v WHERE v.robot_id = [robot_id] ORDER BY id DESC LIMIT 1; 

這樣慢嗎?如果是這樣,讓你的RDBMS(MySQL?)向你解釋查詢計劃。這會告訴你它是否正在進行任何全表掃描,你顯然不想用這麼大的表。您也可以編輯您的問題幷包含valor表的架構供我們查看。

此外,您還可以看到,Django是通過這樣產生(使用由彼得·羅威爾所提供的查詢集)的SQL:

qs = Valor.objects.filter(robot=r).order_by('-id')[0] 
print qs.query 

確保SQL類似於「原始」查詢我貼以上。您還可以讓您的RDBMS向您解釋查詢計劃。

+1

qs.query (或.query.as \ _sql())絕對是任何人試圖從數據庫方面追蹤這個問題的方法。想知道如何限制/ order_by()和filter()的人是在低水平處理時應該玩這個有點 – istruble 2009-12-05 20:29:09

7

此問題的最佳MySQL的語法將沿着線的東西:

SELECT * FROM table WHERE x=y ORDER BY z DESC LIMIT 1 

Django的等效的,這將是:

Valor.objects.filter(robot=r).order_by('-id')[:1][0] 

注意這個解決方案如何利用Django的slicing方法在編譯對象列表之前限制查詢集之前

1

相當快也應該是:

qs = Valor.objects.filter(robot=r) # <-- it doesn't hit the database 
count = qs.count()     # <-- first hit the database, compute a count 
last_item = qs[ count-1 ]   # <-- second hit the database, get specified rownum 

因此,在實踐中,你只需要執行2 SQL查詢;)

0

這樣做的正確方法,是使用內置的查詢集法最新()並將其送入它應該排序的列(字段名稱)。缺點是它只能通過一個數據庫列進行排序。

當前的實現看起來像這樣,並按照@ Aaron的建議進行了優化。

def latest(self, field_name=None): 
    """ 
    Returns the latest object, according to the model's 'get_latest_by' 
    option or optional given field_name. 
    """ 
    latest_by = field_name or self.model._meta.get_latest_by 
    assert bool(latest_by), "latest() requires either a field_name parameter or 'get_latest_by' in the model" 
    assert self.query.can_filter(), \ 
      "Cannot change a query once a slice has been taken." 
    obj = self._clone() 
    obj.query.set_limits(high=1) 
    obj.query.clear_ordering() 
    obj.query.add_ordering('-%s' % latest_by) 
    return obj.get() 
0
Model_Name.objects.first() 

//爲了得到第一元素

Model_name.objects.last() 

//爲了得到最後的()

在我的情況

最後是不行的,因爲在數據庫 只有一排,可以幫助全爲吳太:)