Sql 如何使用带有外键的表在Django中高效地编写不同的查询
我想在前端下拉列表中显示不同的用户城市。为此,我做了一个db查询,它从表Sql 如何使用带有外键的表在Django中高效地编写不同的查询,sql,django,django-models,django-postgresql,Sql,Django,Django Models,Django Postgresql,我想在前端下拉列表中显示不同的用户城市。为此,我做了一个db查询,它从表city中获取不同的city\u name,但只获取用户所在的城市 下面的内容适用于较小的用户表,但如果用户表的大小为1000万,则需要很长时间。不过,这些用户的不同城市仍有约100个 class City(models.Model): city_code = models.IntegerField(unique=True) city_name = models.CharField(max_length=25
city
中获取不同的city\u name
,但只获取用户所在的城市
下面的内容适用于较小的用户
表,但如果用户
表的大小为1000万,则需要很长时间。不过,这些用户的不同城市仍有约100个
class City(models.Model):
city_code = models.IntegerField(unique=True)
city_name = models.CharField(max_length=256)
class User(models.Model):
city = models.ForeignKey('City', to_field='city_code')
现在我尝试搜索不同的城市名称,如下所示:
City.objects.filter().values_list('city__city_name').distinct()
这在PostgreSQL上可以解释为:
SELECT DISTINCT "city"."city_name"
FROM "user"
LEFT OUTER JOIN "city"
ON ("user"."city_id" = "city"."city_code");
时间:9760.302毫秒
这清楚地表明PostgreSQL并没有利用“用户”“城市id”上的索引。我还了解到一个变通解决方案,该解决方案涉及编写以某种方式利用索引的自定义SQL查询
我尝试使用上面的查询查找不同的“user.”“city\u id”,结果证明速度非常快
WITH
RECURSIVE t(n) AS
(SELECT min(city_id)
FROM user
UNION
SELECT
(SELECT city_id
FROM user
WHERE city_id > n order by city_id limit 1)
FROM t
WHERE n is not null)
SELECT n
FROM t;
时间:79.056毫秒
但现在,我发现很难将其合并到Django代码中。我仍然认为在代码中添加自定义查询是一种黑客行为。但是我更担心的是列名可以是完全动态的,我不能在代码中硬编码这些列名(例如city_id等)
#original_fields could be a list from input, like ['area_code__district_code__name']
dataset_klass.objects.filter().values_list(*original_fields).distinct()
使用自定义查询将至少需要拆分字段名,并使用“\uuuuu”作为分隔符,然后处理第一部分。但在我看来,这是一个坏习惯
我该如何改进这一点
注:
城市
用户
示例仅用于解释场景。语法可能不正确。我终于找到了这个解决方案
from django.db import connection, transaction
original_field = 'city__city_name'
dataset_name = 'user'
dataset_klass = eval(camelize(dataset_name))
split_arr = original_field.split("__",1)
"""If a foreign key relation is present
"""
if len(split_arr) > 1:
parent_field = dataset_klass._meta.get_field_by_name(split_arr[0])[0]
cursor = connection.cursor()
"""This query will run fast only if parent_field is indexed (city_id)
"""
cursor.execute('WITH RECURSIVE t(n) AS ( select min({0}) from {1} '
'union select (select {0} from {1} where {0} > n'
' order by {0} limit 1) from t where n is not null) '
'select n from t;'.format(parent_field.get_attname_column()[1], dataset_name))
"""Create a list of all distinct city_id's"""
distinct_values = [single[0] for single in cursor.fetchall()]
"""create a dict of foreign key field to the above list"""
"""to get the actual city_name's using _meta information"""
filter_dict = {parent_field.rel.field_name+'__in':distinct_values}
values = parent_field.rel.to.objects.filter(**filter_dict).values_list(split_arr[1])
else:
values = dataset_klass.objects.filter().values_list(original_field).distinct()
它利用了user
表中city\u id
上的索引,运行速度非常快
WITH
RECURSIVE t(n) AS
(SELECT min(city_id)
FROM user
UNION
SELECT
(SELECT city_id
FROM user
WHERE city_id > n order by city_id limit 1)
FROM t
WHERE n is not null)
SELECT n
FROM t;