외래 키가있는 테이블을 사용하여 Django에서 뚜렷한 쿼리를 효율적으로 쓰는 방법

StackOverflow https://stackoverflow.com//questions/20013116

문제

프런트 엔드 드롭 다운에서 사용자의 별개의 도시를 표시하고 싶습니다. 이를 위해 DB city_name 테이블에서 Distinct City를 가져 오는 DB 쿼리를 만듭니다. 그러나 사용자가있는 도시만을 사용하십시오.

아래의 작은 크기의 User 테이블의 크기가 작지만 User 테이블의 크기가 천만에있는 경우 매우 오랜 시간이 걸립니다. 이 사용자들의 뚜렷한 도시는 여전히 ~ 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 ms

PostgreSQL이 '사용자'에서 인덱스를 사용하지 않았 음을 명확하게 보여주었습니다. 'City_id'. 또한 해결 방법 글쓰기와 관련된 어떻게 든 색인을 활용하는 사용자 정의 SQL 쿼리입니다.

나는 뚜렷한 '사용자'를 찾으려고 노력했다. 위의 쿼리를 사용하고 실제로 꽤 빨리 밝혀졌습니다.

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 ms

그러나 이제는 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()
.

사용자 지정 쿼리를 사용하면 atleast가 구분 기호로 '__'을 사용하여 필드 이름을 분리하고 첫 번째 부분을 처리해야합니다. 그러나 그것은 나에게 나쁜 해킹처럼 보입니다.

어떻게 개선 할 수 있습니까?

ps. City 예제는 시나리오를 설명하는 것으로 표시됩니다. 구문이 올바르지 않을 수 있습니다.

도움이 되었습니까?

해결책

마침내이 해결 방법 해결책에 도달했습니다.

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()
.

city_id 테이블의 user의 인덱스를 사용하는

은 꽤 빨리 실행됩니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top