3个相关表的Django查询优化

时间:2019-12-04 10:14:35

标签: python django postgresql django-queryset

我有4个型号:

class Run(models.Model):
    start_time = models.DateTimeField(db_index=True)
    end_time = models.DateTimeField()
    chamber = models.ForeignKey(Chamber, on_delete=models.CASCADE)
    recipe = models.ForeignKey(Recipe, default=None, blank=True, null=True, on_delete=models.CASCADE)

class RunProperty(models.Model):
    run = models.ForeignKey(Run, on_delete=models.CASCADE)
    property_name = models.CharField(max_length=50)
    property_value = models.CharField(max_length=500)

class RunValue(models.Model):
    run = models.ForeignKey(Run, on_delete=models.CASCADE)
    run_parameter = models.ForeignKey(RunParameter, on_delete=models.CASCADE)
    value = models.FloatField(default=0)

class RunParameter(models.Model):
    parameter = models.ForeignKey(Parameter, on_delete=models.CASCADE)
    chamber = models.ForeignKey(Chamber, on_delete=models.CASCADE)
    param_name_user_defined = models.BooleanField(default=True)

Run可以具有任意数量的RunProperty(通常是用户定义的属性,可以自定义),还可以具有一些预定义的RunValue(例如平均电压,最小电压,最大电压) )是数字值。

RunParameter基本上只是一个包含参数名称(电压,电流,频率,温度,阻抗,振荡,可变性等)的容器。

当我建立前端表以显示每个Run及其所有“文件” RunPropertyRun的来源)和所有“电压” { {1}},我首先在数据库中查询所有RunValue对象,然后再对Min / Max / Avg进行另外3次查询,然后对File进行另一次查询,然后在后端建立一个dict传递到最前面以构建表行:

Run

对于在数据库中拥有约10到30个runs = Run.objects.filter(chamber__in=chambers) min_v_run_values = RunValue.objects.filter(run__in=runs, run_parameter__parameter__parameter_name__icontains="Minimum Voltage") max_v_run_values = RunValue.objects.filter(run__in=runs, run_parameter__parameter__parameter_name__icontains="Maximum Voltage") avg_v_run_values = RunValue.objects.filter(run__in=runs, run_parameter__parameter__parameter_name__icontains="Average Voltage") run_files = RunProperty.objects.filter(run__in=runs, property_name="File") 对象的客户来说,这不是什么大问题,但是我们有一位使用量很大的客户,它拥有3500 Run个实例。不用说,它太慢了。我正在执行5个查询以获取所有需要的实例,然后我必须循环并将它们放到一个字典中。对于一个客户,此过程最多需要45秒(对于大多数其他客户,大约需要8或10秒)。

有没有一种方法可以查询我的数据库中的所有Run对象以及所有的最小/最大/平均电压Run和文件RunValue,然后返回,字典列表,每个RunProperty都有一个字典以及其他对象?

我认为可以在这里使用Run查询,但是我不确定如何使用它们,或者它们是否适用于这种情况?

我尝试了这个(但没有走远):

Q

这使我在1个查询中获得了所有与runs = Run.objects.filter(chamber__in=chambers) v_query = Q(run_parameter__parameter__parameter_name__icontains="Voltage") run_values = RunValue.objects.filter(run__in=runs).filter(v_query) run_files = RunProperty.objects.filter(run__in=runs, property_name="File") 相关的对象,但每个对象仍然有3个查询。如果可能,我需要对此进行更多优化。

我正在寻找符合以下条件的东西:

RunValue

我认为从广义上讲(甚至不是伪代码),我需要一个查询,例如:

“获取所有runs = Run.objects.filter(chamber__in=chambers) .annotate(Q(run__runvalue__run_parameter__parameter__parameter_name__icontains="Voltage") & Q(run__runproperty__property_name__icontains="File")) ,并为每个Run获取与该Run相关的所有RunValue对象,这些对象包含[“ Average”,“ Maximum”, “ Minimum”]以及该Run中包含“文件”的所有RunProperty对象。

我不知道是否有可能(听起来应该是这样),而且我不确定是否应该使用Q过滤,聚合或注释。概括地说,如果可能的话,我需要在一个查询中获得一个模型的所有实例以及每个实例的所有外键

示例:

我有两个实例的表Run

Run

每个R1 R2 实例都有一个与之关联的RunProperty实例“ File”(只是一个字符串):

Run

每个R1_run.dat R2_run.dat 实例都有许多RunValue实例(我以Voltage为例,但其中有26个):

Run

我需要查询数据库以使其返回(列表或字典,我可以解决这两个问题):

R1_max_v
R1_min_v
R1_avg_v

R2_max_v
R2_min_v
R2_avg_v

甚至是2D阵列:

[{R1, R1_run.dat, R1_max_v, R1_min_v, R1_avg_v},
{R2, R2_run.dat, R2_max_v, R2_min_v, R2_avg_v}]

这有可能吗?

2 个答案:

答案 0 :(得分:0)

从数据库的角度来看,您只需使用几个联接的单个查询即可获得所需的所有数据:

-- This assumes that there is a primary key Run.id and 
-- foreign keys RunValue.run_id and RunProperty.run_id.
-- IDs or names of min/max/avg run parameters, as well as 
-- chamber ids are replaced with *_PARAMETER and CHAMBER_IDS 
-- for brevity.
SELECT Run.*, 
       RVmin.value AS min_value, 
       RVmax.value AS max_value,
       RVavg.value AS avg_value,
       RP.value AS file_value
FROM Run 
JOIN RunValue RVmin ON Run.id = RVmin.run_id
JOIN RunValue RVmax ON Run.id = RVmax.run_id
JOIN RunValue RVavg ON Run.id = RVavg.run_id
JOIN RunProperty RP ON Run.id = RP.run_id
WHERE
  RVmin.run_parameter = MIN_PARAMETER AND
  RVmax.run_parameter = MAX_PARAMETER AND
  RVavg.run_parameter = AVG_PARAMETER AND
  RP.property_name = 'File' AND
  Run.chamber IN (CHAMBER_IDS);

Django建立此类联接的方式必须类似于Run.runvalue_set.filter(run_parameter__contains 'Maximum Voltage') 请参阅“向后遵循关系”:https://docs.djangoproject.com/en/2.2/topics/db/queries/#following-relationships-backward

答案 1 :(得分:0)

您可以使用annotateMinMaxAvg在查询中获取此信息。

针对您的问题。你可以做到的。

在ForeignKey字段中添加相关名称。

class RunProperty(models.Model):
    run = models.ForeignKey(Run, on_delete=models.CASCADE, related_name="run_prop_name")

class RunValue(models.Model):
    run = models.ForeignKey(Run, on_delete=models.CASCADE, related_name="run_value_name")
    run_parameter = models.ForeignKey(RunParameter, on_delete=models.CASCADE)
    value = models.FloatField(default=0)

views.py

from django.db.models import Avg, Max, Min

filt = 'run_value_name__value'
query = Run.objects.annotate(run_avg = Avg(filt), run_max = Max(filt))

您可以获得所有值:

  for i in query:
     print(i.run_avg, i.run_max, i.run_min )

-----------编辑------------

请检查我是否在RunValue模型中添加了“ related_name”。

让我们假设您在 Run 模型中有两个值。

1)运行_1

2)运行_2

在模型 RunValue 中,有6个条目。

run = 1,run_parameter =“ Avg_value”,值= 50

运行= 1,运行参数=“最小值”,值= 25

运行= 1,运行参数=“最大值”,值= 75

run = 2,run_parameter =“ Avg_value”,值= 28

run = 2,run_parameter =“最大值”,值= 40

run = 2,run_parameter =“最小值”,value = 16

您想要字典这样的东西:

{'run_1': {'Avg_value': 50, 'Min_value': 25, 'Max_value': 75}, 'run_2': {...}}

记住要阅读select_relatedprefetch_related以获得文档。

    rt = Rub.objects.all().prefetch_related('run_value_name')
    s = {} # output dictionary
    for i in rt:
        s[i.run] = {} # run dictionary
        for j in i.run_value_name.all():

            s[i.run].update({j.run_parameter: j.value}) # update run dictionary

    print(s)

----------添加-----------

检查此代码命中的数据库数。

from django.db import connection, reset_queries
print(len(connection.queries))
reset_queries()
相关问题