Django 쿼리 세트에서 마지막 요소를 빠르게 조회하십니까?
-
13-09-2019 - |
문제
나는 Valor라는 모델입니다. 용기에는 로봇이 있습니다. 나는 다음과 같이 쿼리하고있다 :
Valor.objects.filter(robot=r).reverse()[0]
마지막 용기를 얻으려면 R 로봇. Valor.objects.filter (robot = r) .count ()는 약 200000이고 마지막 항목을 얻는 데는 약 4 초가 걸립니다.
속도를 어떻게 올릴 수 있습니까? 잘못된 길을 쿼리하고 있습니까?
해결책
데이터 세트가 사물을 조금씩 제거하고 싶을 정도로 커질 것 같습니다. 로봇 개체에서 마지막 용기 개체를 추적해 보셨습니까?
class Robot(models.Model):
# ...
last_valor = models.ForeignKey('Valor', null=True, blank=True)
그런 다음 a 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 Objects를 만들 때 추가 DB 트랜잭션 비용을 지불하지만 Last_valor 조회는 빠르게 타격을 줄 것입니다. 그것과 함께 플레이하고 트레이드 오프가 앱에 가치가 있는지 확인하십시오.
다른 팁
이 문제에 대한 최적의 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의 사용 방법에 주목하십시오 슬라이스 쿼리 세트를 제한하는 메소드 ~ 전에 객체 목록을 컴파일합니다.
이전 제안 중 어느 것도 작동하지 않으면 Django를 방정식에서 꺼내고 데이터베이스에 대해이 원시 SQL을 실행하는 것이 좋습니다. 테이블 이름을 추측하고 있으므로 그에 따라 조정해야 할 수도 있습니다.
SELECT * FROM valor v WHERE v.robot_id = [robot_id] ORDER BY id DESC LIMIT 1;
느린가요? 그렇다면 RDBMS (MySQL?)가 귀하에게 쿼리 계획을 설명하십시오. 이것은 전체 테이블 스캔을하고 있는지 알려줄 것입니다. 질문을 편집하고 스키마를 포함시킬 수도 있습니다. valor
우리가 볼 수있는 테이블.
또한 Django 가이 작업을 수행하여 생성하는 SQL을 볼 수 있습니다 (Peter Rowell이 제공 한 쿼리 세트).
qs = Valor.objects.filter(robot=r).order_by('-id')[0]
print qs.query
SQL이 위에 게시 한 '원시'쿼리와 유사한 지 확인하십시오. RDBMS에서 쿼리 계획을 설명 할 수도 있습니다.
글쎄, order_by 절은 없으므로 '마지막'이 무슨 뜻인지 궁금합니다. 당신이 '마지막 추가'를 의미한다고 가정하면
Valor.objects.filter(robot=r).order_by('-id')[0]
당신을 위해 일을 할 수 있습니다.
django 1.6은 .first () 및 .last ()를 소개합니다.
https://docs.djangoproject.com/en/1.6/ref/models/querysets/#last
그래서 당신은 단순히 할 수 있습니다 :
Valor.objects.filter(robot=r).last()
상당히 빠른 것도 다음과 같아야합니다.
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 쿼리 만 실행합니다.)
Django에 한계 조항이 있습니까? 이렇게하면 DB를 가질 수 있습니다. 단일 레코드를 반환하십시오.
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로 번역되지 않았다는 것을 알고 있지만 누군가가 돌아와서 이것을 청소할 수 있습니다.
이 작업을 수행하는 올바른 방법은 내장 쿼리 세트 메소드를 사용하여 최신 ()을 사용하여 정렬 해야하는 열 (필드 이름)을 공급하는 것입니다. 단점은 단일 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()
Model_Name.objects.first()
// 첫 번째 요소를 얻으려면
Model_name.objects.last()
// get last ()
내 경우에는 마지막으로 데이터베이스에 행이 하나만 있으면 작동하지 않습니다. :)