كيفية كتابة استعلام DISTINCT بكفاءة في Django مع وجود جدول يحتوي على مفاتيح خارجية

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

سؤال

أريد إظهار المدن المميزة للمستخدمين في القائمة المنسدلة للواجهة الأمامية.لذلك، أقوم بإجراء استعلام قاعدة البيانات الذي يجلب مميزًا city_name من الجدول City ولكن فقط تلك المدن التي يتواجد فيها المستخدمون.

شيء مثل أدناه يعمل لحجم صغير User الجدول، ولكن يستغرق وقتا طويلا جدا إذا User طاولة بحجم 10 مليونلا تزال المدن المميزة لهؤلاء المستخدمين تصل إلى 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 لم يكن يستخدم الفهرس الموجود على "user".'city_id'.قرأت أيضًا عن الحل البديل هنا والتي تضمنت كتابة استعلام SQL مخصص يستخدم بطريقة أو بأخرى ملف Index.

لقد حاولت العثور على 'user'.'city_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 مللي ثانية

ولكن الآن أجد صعوبة في دمج هذا في كود جانغو الخاص بي.ما زلت أعتقد أنه نوع من الاختراق بإضافة استعلام مخصص في الكود لهذا الغرض.لكن ما يثير القلق الأكبر بالنسبة لي هو أن اسم العمود يمكن أن يكون ديناميكيًا تمامًا، ولا يمكنني ترميز أسماء الأعمدة هذه (على سبيل المثال.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()

سيتطلب استخدام الاستعلام المخصص على الأقل تقسيم اسم الحقل باستخدام "__" كمحدد ومعالجة الجزء الأول.ولكن يبدو وكأنه اختراق سيء بالنسبة لي.

كيف يمكنني تحسين هذا؟

ملاحظة.ال City User يظهر المثال فقط لشرح السيناريو.قد لا يكون بناء الجملة صحيحًا.

هل كانت مفيدة؟

المحلول

لقد وصلت أخيرًا إلى هذا الحل البديل.

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