如何在Django中进行查询时减少数据库命中次数

时间:2019-03-09 05:31:15

标签: mysql django django-models

我有三个桌子

  1. 用户
  2. 设备
  3. 登录

我想根据设备和日志过滤日志。我正在使用以下查询,该查询遍历用户和设备以获取日志。我觉得这会成为性能上的打击。如何减少数据库命中次数?

for user_obj in User.objects.all():
    device_qs = Device.objects.filter(user=user_obj)
    if device_qs.exists():
        for device_obj in device_qs:
            log_count = Log.objects.filter(user=user_obj, device=device_obj, created_at__range(from_date, to_date)).count()

2 个答案:

答案 0 :(得分:1)

我要做的是在您的MySQL实例中创建一个引用view的“代理模型”

视图如下:

SELECT 
t1.*,
t2.*,
t3.*
FROM users t1
RIGHT JOIN device t2 (ON t1.id=t2.user_id)
RIGHT JOIN log t3 (ON t3.device_id=t2.id);

现在要创建代理模型,请执行以下操作:

class SomeModel(models.Model):
    # all fields from the 3 tables here

    class Meta:
        db_table = 'yourViewNameHere'
        managed = False # this keeps django from creating the table

然后像往常一样python manage.py makemigrations + python manage.py migrate

现在,要访问所需的数据,您将执行以下操作:

from django.db import connection
sql = "SELECT * FROM your_view WHERE some_date_column > 'foo' AND some_date_column < 'bar' "

with connection.cursor() as cur:

    cur.execute(sql)
    data = cur.fetchall()

print(data)

注意,如果您要将参数传递给原始sql查询,则应始终像这样传递参数以避免sql注入:

sql = "SELECT * FROM your_view WHERE some_date_column > %s AND some_date_column < %s"

params = ('foo', 'bar')
with connection.cursor() as cur:

    cur.execute(sql, params)
    data = cur.fetchall()

答案 1 :(得分:1)

如果您只需要每个用户和设备的日志计数(这是您从发布的代码中获得的计数),则可以在一个查询中获得该计数:

from django.db.models import Count

logs = (Log.objects
    .filter(created_at__range = (from_date, to_date))
    .values('user', 'device')
    .annotate(log_count=Count('device'))
)

您可以修改查询以包括所需的用户和设备模型的任何属性:

.values('user__last_name', 'device__name')  # etc.

您还可以通过在末尾附加order_by()来对数据集进行排序,以便能够按所需顺序对其进行迭代:

.order_by('user__last_name', '-log_count')