Могу ли я заставить Doctrine ORM разделить сложный запрос? - PullRequest
0 голосов
/ 19 ноября 2010

Я делаю запрос с Doctrine, который содержит много объединений. Некоторые из них имеют отношения hasOne, а некоторые - hasMany.

Я думаю, что CakePHP делает отдельные запросы для каждого отношения hasMany, но Doctrine, похоже, выполняет один огромный запрос. Оба могут гидрировать данные и возвращать хороший массив в ваш php-скрипт, но они, похоже, делают для этого разные наборы запросов. С Doctrine, как только ваш запрос содержит несколько соединений hasMany, производительность может стать довольно ужасной.

В CakePHP по умолчанию используется разделение запроса, но я могу заставить его объединиться (http://book.cakephp.org/view/872/Joining-tables). Есть ли способ сделать обратное в Doctrine: заставить его разделить объединения hasMany на разные запросы? пробовал документы и API, но пока ничего не нашел.

Ответы [ 3 ]

2 голосов
/ 22 ноября 2010

Неправильное решение: запросы со многими объединениями одновременно должны всегда выполняться быстрее, чем использование фрагментированных запросов в нескольких операторах SQL, при условии, что ваш запрос правильный.

Если ваш запрос ужасно медленный, как только вы добавляете n: n или 1: n объединений, в Doctrine есть несколько причин для этого.

Одной из наиболее частых ошибок в запросах с несколькими объединениями является использование конструкции LEFT JOIN + WHERE, в которой вы можете использовать INNER JOIN с ON. Рассмотрим пример DQL:

SELECT a.*, c.*
  FROM Article a
  LEFT JOIN a.Buyer b
  LEFT JOIN b.Creditcard c
  LEFT JOIN c.digitalinfo d
  WHERE b.id = 2 AND d.info LIKE 'TEST%'

Это очень медленный запрос, если во всех таблицах 10000 записей. Сначала она объединит всю таблицу b с таблицей a, что приведет к 10000 ^ 2 строкам, тогда как в предложении WHERE вы отбрасываете почти 99,9% из них всех.

SELECT a.*, c.*
  FROM Article a
  INNER JOIN a.Buyer b WITH b.id=2
  LEFT JOIN b.Creditcard c
  INNER JOIN c.Digitalinfo d WITH d.info LIKE 'TEST%'

Здесь ВНУТРЕННЕЕ СОЕДИНЕНИЕ a.Buyer b не заканчивается 100 000 000 строк, но, поскольку оно использует расширенное предложение ON (Doctrine называет это WITH), оно оставляет только небольшой набор. Поэтому два других соединения будут молниеносными по сравнению с тем, что они выполняли, как в другом утверждении.

Также убедитесь, что у вас ВСЕГДА есть индексы

  • В столбцах, по которым вы ведете поиск. (Если вы выполняете поиск по полному имени, как в FirstName + '' + LastName, создайте индекс для этой последовательности!)
  • В столбцах, для которых вы выполняете определенные объединения
  • Для внешних ключей и полей, для которых установлена ​​ссылка.

Если вы хотите узнать, что ваша база данных делает за кулисами, вы можете, например, ввести в MySQL тип EXPLAIN, за которым следует ваш запрос, и он точно скажет вам, почему это занимает так много времени.

0 голосов
/ 24 июля 2011

Я тоже столкнулся с этой проблемой.У меня был запрос, который занимал 0,0060 секунд в исходном виде, но для которого процесс гидратации массива занимал 8 секунд.Запрос возвратил 2180 строк из-за нескольких левых соединений (это был один объект со всеми его связями).

После решения @ Elvis я сократил время гидратации до 0,3 секунды.Я просто разделил запрос на два отдельных запроса, в результате чего первый из них содержал 60 записей, а остальные 30 записей.

0 голосов
/ 13 июня 2011

Doctrine позволяет объединять несколько таблиц, пересекая более одного отношения oneToMany.Это приводит к запросу с экспоненциальным (по числу присоединяемых таблиц) количеством записей.Процесс гидратации затем уточняет набор результатов и составляет дерево из вашего набора результатов с двумя измерениями (таблица).Это целесообразно для небольшого набора результатов и нескольких таблиц, к которым присоединяются.

Если у вас есть соответствующее количество таблиц и записей, вы должны выполнять отдельные запросы.

...