Python 针对3个相关表的Django查询优化
我有4种型号:Python 针对3个相关表的Django查询优化,python,django,postgresql,django-queryset,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=
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
基本上只是一个参数名称(电压、电流、频率、温度、阻抗、振荡、可变性等)的容器,有很多
当我构建前端表以显示每个运行
及其所有“文件”RunProperty
(运行的来源)和所有“电压”时RunValue
,我首先查询数据库中的所有Run
对象,然后再查询3次Min/Max/Avg,然后再查询一次文件,然后在后端构建一个dict传递到前端以构建表行:
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")
对于数据库中有10到30个Run
对象的客户来说,这并不是什么大问题,但我们有一个使用率很高的客户,他有3500个Run
实例。不用说,速度太慢了。我正在进行5次查询,以获得所有需要的实例,然后我必须循环并将它们放在一个目录中。这需要我们为一个客户执行此操作需要45秒的时间(大多数其他客户大约需要8秒或10秒)
有没有一种方法可以让我查询数据库中的所有Run
对象,以及所有的最小/最大/平均电压RunValue
和文件RunProperty
,然后返回一个dict列表,每个Run
和其他对象对应一个dict
我认为这里可以使用Q
查询,但我不太确定如何使用它们,或者它们是否适用于此场景
我试过这个(但没有成功):
这让我在一个查询中获得所有与RunValue
相关的对象,但每个查询仍然有3个。如果可能的话,我需要对此进行更多优化
我正在寻找以下内容:
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"))
我认为从广义上讲(甚至不是伪代码),我需要一个如下的查询:
“获取所有运行
s,对于每个运行
,获取与该运行
相关的包含[“平均”、“最大”、“最小”]的所有运行
对象,以及该运行
包含“文件”的所有运行属性
对象。”
我不知道这是否可能(听起来应该是这样),我也不确定是否应该使用Q过滤、聚合或注释。概括地说,如果可能的话,我需要在一个查询中获得一个模型的所有实例,以及每个实例的所有外键
例如:
我有表运行
,有两个实例:
R1
R2
每个Run
实例都有一个关联的RunProperty实例“文件”(仅一个字符串),用于:
R1_run.dat
R2_run.dat
每个Run
实例都有许多RunValue实例(我使用Voltage作为示例,但有26个):
我需要查询数据库,以便它返回(list或dict,我可以解决这两种情况之一):
甚至是二维阵列:
[[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]]
这可能吗?从数据库的角度来看,只需使用一个查询和几个连接即可获得所需的所有数据:
-- 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\u set.filter(Run\u参数\u包含“最大电压”)
请参阅“以下向后关系”:从数据库的角度来看,只需使用一个查询和几个连接即可获得所需的所有数据:
-- 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\u set.filter(Run\u参数\u包含“最大电压”)
请参阅“以下向后关系”:您可以在查询中使用
注释,最小值,最大值,平均值
为了你的问题,你可以这样做
在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模型中添加了“相关名称”
让我们假设您在运行模型中有两个值
1) 运行1
2) 运行2
在模型运行值中,有6个条目
运行=1,运行参数=“平均值”,值=50
运行=1,运行参数=“最小值”,值=25
运行=1,运行参数=“最大值”,值=75
运行=2,运行参数=“平均值”,值=28
运行=2,运行参数=“最大值”,值=40
运行=2,运行参数=“最小值”,值=16
您需要这样的词典:
{'run_1': {'Avg_value': 50, 'Min_value': 25, 'Max_value': 75}, 'run_2': {...}}
要执行此操作,请记住阅读文档的选择与_相关的
和预取与_相关的
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()
通过使用注释
,最小值
,最大值
,平均值
,您可以在查询中获得此信息
为了你的问题,你可以这样做
在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模型中添加了“相关名称”
让我们假设您在运行模型中有两个值
1) 运行1
2) 运行2
在模型运行值中,有6个条目
运行=1,运行参数=“平均值”,值=50
运行=1,运行参数=“最小值”,值=25
运行=1,运行参数=“最大值”,值=75
运行=2,运行参数=“平均值”,值=28
运行=2,运行参数=“最大值”,值=40
运行=2,运行参数=“最小值”,值=16
您需要这样的词典:
{'run_1': {'Avg_value': 50, 'Min_value': 25, 'Max_value': 75}, 'run_2': {...}}
要执行此操作,请记住阅读文档的选择与_相关的
和预取与_相关的
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)
------添加------------
检查数据库hi的编号