Набор запросов Django: как изменить возвращенную структуру данных - PullRequest
0 голосов
/ 13 июня 2018

Эта проблема связана с игровым аркадным салоном, где люди идут в салон и играют в игру.Когда человек играет, в базе данных создается новая запись.Моя модель выглядит так:

class gaming_machine(models.Model):
  machine_no = models.Integer()
  score = models.Integer()
  created = models.DateTimeField(auto_now_add=True)

Мой взгляд таков:

today = datetime.now().date()

# i am querying the db for getting the gaming_machine objects where score = 192 or 100 and the count of these objects separately for gaming_machines object which have 192 score and gaming_machine objects which have score as 100 

gaming_machine.objects.filter(Q(points=100) | Q(points=192),created__startswith=today).values_list('machine_no','points').annotate(Count('machine_no'))
# this returns a list of tuples -> (machine_no, points, count)
<QuerySet [(330, 192,2), (330, 100,4), (331, 192,7),(331,192,8)]>
  1. Могу ли я изменить формат возвращенного набора запросов на что-то вроде этого: {(330, 192):2, (330, 100) :4, (331, 192):7,(331,192):8} # that is a dictionary with a key as a tuple consisting (machine_no,score) and value as count of such machine_nos
  2. Мне известно, что я могу изменить формат этого набора запросов на стороне Python, используя что-то вроде словарного понимания, но я не могу этого сделать, поскольку это занимает около 1,4 секунды, потому что наборы запросов Django ленивы.

1 Ответ

0 голосов
/ 19 июня 2018

Django lazy запросов ...

но я не могу этого сделать, так как это занимает около 1,4 секунды, потому что наборы запросов Django ленивы.

Лень запросов Django фактически не оказывает (почти) никакого влияния на производительность.Они ленивы в том смысле, что они откладывают , запрашивая базу данных до , вам нужен результат (например, когда вы начинаете итерацию по нему).Но тогда они извлекут все строки.Таким образом, при каждом извлечении следующей строки нет издержек, все строки извлекаются, и затем Python выполняет итерацию по ней довольно быстро.

Таким образом, лень составляет , а не для строки за строкой.основание строки: не перемещает курсор каждый раз, когда вы хотите получить следующую строку.Таким образом, связь с базой данных (вполне) ограничена.

... и почему она не имеет значение (с точки зрения производительности)

Если количество строк неОгромный (50 000 или более), переход к словарю также должен произойти довольно быстро.Поэтому я подозреваю, что издержки, вероятно, связаны с самим запросом.Тем более, что Django должен «десериализовать» элементы: превратить ответ в кортежи, поэтому, хотя может иметь дополнительные издержки, обычно это будет разумно по сравнению с работой, которая уже выполняется без словарь понимания.Обычно один кодирует задачи в запросах, если они приводят к минус данным, которые передаются в Python.

Например, выполняя подсчет в базе данных, база данных будет возвращать целое число на строку вместофильтруя несколько строк, мы также уменьшаем количество строк (поскольку обычно не все строки соответствуют заданным критериям).Кроме того, в базе данных обычно есть механизмы быстрого поиска, которые увеличивают WHERE с, GROUP BY с, ORDER BY с и т. Д. Но постобработка потока для другого объекта обычно требует такой же величины времени для базы данных.

Таким образом, словарь должен понимать:

<b>{
    d[:2]: d[3]
    for d in</b> gaming_machine.objects.filter(
                  Q(points=100) | Q(points=192),created__startswith=today
             ).values_list(
                 'machine_no','points'
             ).annotate(
                 Count('machine_no')
             )
<b>}</b>

Ускорение запросов

Поскольку проблема, вероятно, находится в базе данных, вы, вероятно, захотите рассмотреть некоторые возможности для ускорения.

Создание индексов

Как правило, лучший способ повысить производительность запросов - это создать index для столбцов, которые вы фильтруете по часто , ииметь большое число различных значений.

В этом случае база данных создаст структуру данных, которая хранит для каждого значения этого столбца список строк, соответствующих этому значению.Таким образом, в результате вместо чтения всех строк и выбора соответствующих, база данных может мгновенно получить доступ к структуре данных и, как правило, узнать в разумные сроки, какие строки имеют это значение.

Обратите внимание, что это обычно только помогаетесли столбец содержит большое количество различных значений: если, например, столбец содержит только два значения (в 1% случаев значение равно 0, а в 99% случаев 1) и мы фильтруем по очень распространенному значению, это не даст большого ускорения, поскольку набор, который мы должны обработать, имеет примерно одинаковый размер.

Таким образом, в зависимости от того, насколько различны значения, мы можем добавитьиндексы для полей points и created:

class gaming_machine(models.Model):
  machine_no = models.Integer()
  score = models.Integer(<b>db_index=True</b>)
  created = models.DateTimeField(auto_now_add=True<b>, db_index=True</b>)

Улучшение запроса

Во-вторых, мы также можем стремиться улучшить сам запрос, хотя это может зависеть от базы данных.(если у нас есть два запроса q1 и q2, то возможно, что q1 работает быстрее, чем q2 в базе данных MySQL, и q2 работает для примерабыстрее чем q1 в базе данных PostgreSQL).Так что это довольно сложно: конечно, есть вещи, которые обычно работают в целом, но трудно дать гарантии.

Например, иногда x IN (100, 192) работает быстрее, чем x = 100 OR x = 192 (см. здесь ).Кроме того, здесь вы используете __startswith, который может работать хорошо - в зависимости от того, как база данных хранит временные метки - но это может привести к вычислительно дорогостоящему запросу, если сначала нужно преобразовать datetime.В любом случае, использование created__date более декларативно, так как дает понять, что вы хотите, чтобы дата created была равна сегодняшнему, поэтому, возможно, более эффективный запрос:

{
    d[:2]: d[3]
    for d in gaming_machine.objects.filter(
                  <b>points__in=[100, 192], created__date=today</b>
             ).values_list(
                 'machine_no','points'
             ).annotate(
                 Count('machine_no')
             )
}
...