Лишние JOIN в SQL запросах

от автора

Делая отладку производительности небольшого проекта, но с достаточно большой базой, столкнулся с неприятным спецэффектом.
Django при выборках с условиями по внешним ключам, связанным с проверкой на NULL, генерирует запросы, содержащие JOIN по каждому такому ключу. К примеру, для модели

class ForumPlugin(models.Model):     name = models.CharField(         null=False,          blank=False,          max_length=50,          unique=True,          verbose_name=_('name')     )  class Thread(MPTTModel):        parent = TreeForeignKey(         'self',          null=True,          blank=True,          related_name='children',          verbose_name=_('parent thread')     )     plugin = models.ForeignKey(         ForumPlugin,          null=True,          blank=True,          related_name='threads',          verbose_name=_('plugin')     )

При выполнении выборки

Thread.objects.filter(plugin_isnull=True, parent_isnull=True)

Djando формирует такой запрос:

SELECT `forum_thread`.`id`, `forum_thread`.`parent_id`, `forum_thread`.`plugin_id`, `forum_thread`.`lft`, `forum_thread`.`rght`, `forum_thread`.`tree_id`, `forum_thread`.`level` FROM `forum_thread` LEFT OUTER JOIN `forum_thread` T2 ON (`forum_thread`.`parent_id` = T2.`id`) LEFT OUTER JOIN `forum_forumplugin` ON (`forum_thread`.`plugin_id` = `forum_forumplugin`.`id`) WHERE (T2.`id` IS NULL AND `forum_forumplugin`.`id` IS NULL AND ) ORDER BY `forum_thread`.`id

Естественно время исполнения такого запроса увеличивается на несколько порядков, что при больших таблицах может быть критично. Так на моем проекте на таблице порядка 20-30к записей такая выборка вместо положенной 1мс выполняется от 100мс до 300мс, что увеличивает время генерации страницы вдвое.

К сожалению бага разработчикам ORM известна уже четыре года и имеет долгую и печальную историю.

В данный момент присутствует во всех стабильных версиях, в том числе в 1.4.3. Предполагается, что в 1.5 она наконец-то будет исправлена.

В качестве обходного пути советуют использовать двойное отрицание:

Thread.objects.exclude(plugin_isnull=False, parent_isnull=False)

но мне не удалось на практике таким способом избавится от проблемы. Обращение напрямую к полю parent_id также не помогает.

Будьте внимательны при проектировании моделей и старайтесь учитывать эту особенность Django, и избегать выборок по внешним ключам с использованием NULL условий.

ссылка на оригинал статьи http://habrahabr.ru/post/165895/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *