Valorというモデルを持っています。バロールにはロボットがいます。私はこのように問い合わせています:
Valor.objects.filter(robot=r).reverse()[0]
r ロボットの最後のバロールを取得します。Valor.objects.filter(robot=r).count() は約 200000 で、私の PC では最後のアイテムを取得するのに約 4 秒かかります。
どうすれば高速化できますか?間違った方法でクエリを実行していますか?
Valorというモデルを持っています。バロールにはロボットがいます。私はこのように問い合わせています:
Valor.objects.filter(robot=r).reverse()[0]
r ロボットの最後のバロールを取得します。Valor.objects.filter(robot=r).count() は約 200000 で、私の PC では最後のアイテムを取得するのに約 4 秒かかります。
どうすれば高速化できますか?間違った方法でクエリを実行していますか?
この問題に対する最適な 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 のスライシングメソッドを使用してクエリセットを制限する方法に注意してください。
データセットが十分に大きくなり、物事を少し非正規化したくなるようです。Robot オブジェクトの最後の Valor オブジェクトを追跡しようとしましたか?
class Robot(models.Model):
# ...
last_valor = models.ForeignKey('Valor', null=True, blank=True)
そして、post_save シグナルを使用して更新を行います。
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)
Valor オブジェクトを作成するときに追加の db トランザクションのコストを支払うことになりますが、last_valor ルックアップは非常に高速です。それを試して、トレードオフがアプリにとって価値があるかどうかを確認してください。
django 1.6 では .first() と .last() が導入されました。
https://docs.djangoproject.com/en/1.6/ref/models/querysets/#last
したがって、次のように簡単に実行できます。
Valor.objects.filter(robot=r).last()
まあ、order_by句がないので、「最後」とはどういう意味なのか気になります。「最後に追加された」という意味であると仮定すると、
Valor.objects.filter(robot=r).order_by('-id')[0]
あなたのために仕事をするかもしれません。
また、非常に高速である必要があります。
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 クエリのみを実行します ;)
Model_Name.objects.first()
//最初の要素を取得する場合
Model_name.objects.last()
// get last() の場合
私の場合、データベースには行が1つしかないため、最後は機能しません:)
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 に翻訳されていないことはわかっていますが、誰かが戻ってきてこれをクリーンアップできます。
これを行う正しい方法は、組み込みの QuerySet メソッド latest() を使用して、ソートする列 (フィールド名) をフィードすることです。欠点は、単一の db 列でしかソートできないことです。
現在の実装は次のようになり、@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()