Я хочу показать разные города пользователей в раскрывающемся списке переднего плана. Для этого я делаю запрос к базе данных, который извлекает отдельные 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 мс
Но теперь мне трудно включить это в мой код 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()
Для использования пользовательского запроса потребуется как минимум разделить имя поля с «__» в качестве разделителя и обработать первую часть. Но мне это кажется плохим взломом.
Как я могу улучшить это?
PS. Пример City
User
показан только для пояснения сценария. Синтаксис может быть неправильным.