Джанго: Почему Foo.objects.extra (...) намного быстрее, чем Foo.objects.raw? - PullRequest
5 голосов
/ 03 октября 2011

Итак, я пытаюсь оптимизировать довольно странный запрос, но это устаревшая база данных, поэтому я справляюсь с тем, что имею.Это запросы, которые я пытаюсь.Они обеспечивают одинаковый выход на данный момент.w - это мой набор запросов.

def future_schedule(request):

    past = datetime.date.today()-datetime.timedelta(days=730)

    extra_select = {
        'addlcomplete': 'SELECT Complete FROM tblAdditionalDates WHERE Checkin.ShortSampleID = tblAdditionalDates.ShortSampleID',
        'addldate': 'SELECT AddlDate FROM tblAdditionalDates WHERE Checkin.ShortSampleID = tblAdditionalDates.ShortSampleID'
    }
    extra_where = ['''(Checkin.Description <> "Sterilization Permit" AND Checkin.Description <> "Registration State" AND Checkin.Description <> "Miscellaneous" AND Checkin.Description <> "Equipment Purchase" AND Checkin.DateArrived > %s AND Checkin.DateCompleted IS NULL AND Checkin.Canceled = 0) OR (Checkin.Description <> "Sterilization Permit" AND Checkin.Description <> "Registration State" AND Checkin.Description <> "Miscellaneous" AND Checkin.Description <> "Equipment Purchase" AND Checkin.DateArrived > %s AND Checkin.DateCompleted IS NOT NULL AND Checkin.DateFinalCompleted IS NULL AND Checkin.DateFinalExpected IS NOT NULL AND Checkin.Canceled = 0) '''
    ]
    extra_params = [past, past]

    w = Checkin.objects.extra(select=extra_select, where=extra_where, params=extra_params)

# OR This one

    w = Checkin.objects.raw('''SELECT Checkin.SampleID, Checkin.ShortSampleID, Checkin.Company, A.Complete, Checkin.HasDates, A.AddlDate FROM Checkin LEFT JOIN (SELECT ShortSampleID, Complete, AddlDate FROM tblAdditionalDates) A ON A.ShortSampleID = Checkin.ShortSampleID WHERE (Checkin.Description <> "Sterilization Permit" AND Checkin.Description <> "Registration State" AND Checkin.Description <> "Miscellaneous" AND Checkin.Description <> "Equipment Purchase" AND Checkin.DateArrived > "2009-01-01" AND Checkin.DateCompleted IS NULL AND Checkin.Canceled = 0) OR (Checkin.Description <> "Sterilization Permit" AND Checkin.Description <> "Registration State" AND Checkin.Description <> "Miscellaneous" AND Checkin.Description <> "Equipment Purchase" AND Checkin.DateArrived > "2009-01-01" AND Checkin.DateCompleted IS NOT NULL AND Checkin.DateFinalCompleted IS NULL AND Checkin.DateFinalExpected IS NOT NULL AND Checkin.Canceled = 0)''')

Обе они возвращают одинаковое количество записей (322)..extra отрисовывает HTML примерно на 10 секунд быстрее, чем запрос .raw, и для всех интенсивных целей запрос .raw даже менее сложен.Кто-нибудь знает, почему это может быть?Исходя из моей структуры, .raw может быть единственным способом получения нужных мне данных (мне нужны addlcomplete и addldate в dict extra_select и использовать их в предложении Have для дальнейшей фильтрации набора запросов), но мне, конечно, не нравится, какдолго это займет.Это на уровне шаблона, что это медленнее или фактический слой запроса?Как мне лучше всего отладить это?

Спасибо за вашу помощь в этом поиске оптимизации среди плохих структур данных.

ОБНОВЛЕНИЕ 1: 2011-10-03

Итак, я установил django-debugtoolbar, чтобы немного покопаться, и включил общее ведение журнала MySQL и получил следующее:

с использованием .filter() или .extra() Общее количество запросов равно 2. Использование .raw() Общее количество запросов 1984 !!! (жуткая литературная ссылка не игнорируется)

Мой шаблон использует перегруппировку, а затем перебирает эту перегруппировку.Никакие отношения не соблюдаются, никакие теги шаблона, кроме встроенных, не используются.Select_related НЕ используется, и я все еще получаю только 2 запроса.Глядя на журнал mysql, достаточно точно - 1984 запросов.

При взгляде на запросы, которые были выполнены, в основном это выглядит так, как будто каждый {{ Modelinstance.field }} django делал SELECT pk, field FROM Model WHERE Model.pk = Modelinstance.pk Это кажется совершенно неправильным, если вы спроситемне.Я что-то здесь упускаю или django действительно сходит с ума с запросами?

END UPDATE 1

UPDATE 2 См. Ответ ниже

Грег

Ответы [ 2 ]

4 голосов
/ 06 октября 2011

Хорошо.Вот мои окончательные выводы.Несмотря на то, что Furbeenator прав в отношении внутренних оптимизаций Django, выясняется, что существует гораздо большая пользовательская ошибка, которая вызвала замедление и вышеупомянутые тысячи запросов.

Это четко задокументировано в необработанных документах наборов запросов что, когда вы откладываете поля (то есть не используете SELECT * FROM ...) и выбираете только определенные поля (SELECT Checkin.Sampleid, ..., к полям, которые вы не выбираете, можно получить доступ, но с помощью другого вызова базы данных. Итак, если вы выбираетеПодмножество полей в вашем необработанном запросе, и вы забыли поле в запросе, которое вы используете в своем шаблоне, Django выполняет поиск в базе данных, чтобы найти то поле, на которое вы ссылаетесь в своем шаблоне, вместо того, чтобы жаловаться на то, что оно не существует или что-то еще.скажем, вы пропустили 5 полей из вашего запроса (что я и сделал), на который вы в конечном итоге ссылаетесь в своем шаблоне, и у вас есть 300 записей, по которым вы проходите цикл. Это вызывает 1500 дополнительных обращений к базе данных, чтобы получить эти 5 полей для каждой записи.

Итак,остерегайтесь скрытых ссылок и слава богу за Django Debug Toolbar

1 голос
/ 03 октября 2011

Из раздела Оптимизация: Оптимизация доступа к базе данных , они предлагают способы оптимизации, одним из которых является метод extra (). Затем они упоминают .raw (). Я предполагаю, что они сделали raw () намного более надежным и мощным, поэтому он предлагал максимальную гибкость по сравнению с оптимизацией. Выполнение необработанных SQL-запросов позволяет вам делать гораздо больше, чем просто (). Я догадываюсь, что он больше ориентирован на гибкость, чем на производительность, и по возможности следует использовать extra () вместо raw ().

...