Doctrine2: Ограничение с помощью левых соединений/пагинации — передовой опыт

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

$dql = 'SELECT blogpost, comment, tags 
FROM BlogPost blogpost 
LEFT JOIN blogpost.comments comments
LEFT JOIN blogpost.tags tags';

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

$result = $em->createQuery($dql)->setMaxResults(15)->getResult();

Использование едва документированного решения разбивки на страницы который поставляется с доктриной 2.2, на самом деле не работает для меня, поскольку он настолько медленный, что я мог бы также загрузить все данные.

Я попробовал решения в статье Stackoverflow, но даже в этой статье по-прежнему отсутствует передовой опыт, а представленное решение ужасно медленное. .

Разве нет лучшей практики, как это сделать? Никто не использует Doctrine2.2 в режиме производства?


person Andresch Serj    schedule 22.05.2012    source источник
comment
Пожалуйста, добавьте код, который у вас есть, к вашему вопросу, возможно, даже с примерами результатов, чтобы показать, что вы хотите и что вы получаете.   -  person Gustav Bertram    schedule 07.06.2012


Ответы (2)


Получить правильные результаты с таким запросом проблематично. На веб-сайте Doctrine есть учебник, объясняющий эту проблему.

Разбиение на страницы

Учебное пособие больше посвящено нумерации страниц, а не получению первых 5 результатов, но общая идея заключается в том, что вам нужно выполнить «ВЫБЕРИТЕ ОТЛИЧНЫЕ a.id ОТ статей a ... LIMIT 5» вместо обычного SELECT. Это немного сложнее, чем это, но последние 2 пункта в этом руководстве должны указать вам правильный путь.

Обновление:

Проблема здесь не в Doctrine или любом другом ORM. Проблема заключается непосредственно в том, что база данных может вернуть результаты, которые вы запрашиваете. Именно так работают соединения.

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

Основываясь на том, что обсуждается в статье о разбивке на страницы, может показаться, что вам нужно как минимум 2 запроса, чтобы получить желаемые результаты. Добавление DISTINCT к запросу может значительно замедлить ваш запрос, но это действительно необходимо только в том случае, если в нем есть объединения. Вы можете написать другой запрос, который просто извлекает первые 10 сообщений, упорядоченных по дате создания, без соединений. Получив идентификаторы этих 10 сообщений, выполните еще один запрос со своими соединениями и WHERE blogpost.id IN (...) ORDER BY blogpost.created. Этот метод должен быть намного эффективнее.

SELECT 
    bp 
FROM 
    Blogpost bp 
ORDER BY 
    bp.created DESC
LIMIT 10

Поскольку все, что вам нужно в первом запросе, это идентификаторы, вы можете настроить Doctrine на использование Scalar Hydration.

SELECT 
    bg 
FROM 
    Blogpost bp 
LEFT JOIN 
    bp.comments c 
LEFT JOIN 
    bp.tags t 
WHERE 
    bp.id IN (...) 
ORDER BY 
    bp.created DESC

Вероятно, вы также могли бы сделать это в одном запросе, используя коррелированный подзапрос. Миф о том, что подзапросы всегда плохи, НЕ соответствует действительности. Иногда они быстрее соединений. Вам нужно будет поэкспериментировать, чтобы найти лучшее решение для вас.

person Bart Wegrzyn    schedule 08.06.2012

Изменить в свете выясненного вопроса:

Вы можете делать то, что хотите, в собственном MySQL, используя подзапрос в предложении FROM как таковой:

SELECT * FROM 
 (SELECT * FROM articles ORDER BY date LIMIT 5) AS limited_articles, 
 comments, 
 tags 
WHERE
 limited_articles.article_id=comments.article_id
 limited_articles.article_id=tags.article_id

Насколько мне известно, DQL не поддерживает подобные подзапросы, поэтому вы можете использовать класс NativeQuery.

person Nicolas Renold    schedule 07.06.2012
comment
Нет, мне не нужна самая последняя M из моей N. Мне нужна самая последняя N со всеми их M, X и Y. - person Andresch Serj; 12.06.2012
comment
Я не большой поклонник использования DABL/ORM Framework, а затем все еще использую собственные запросы. Для меня это не имеет особого смысла. - person Andresch Serj; 18.06.2012
comment
Тогда, похоже, вам нужно будет сделать два запроса. Я согласен с тем, что должен быть лучший способ ограничить и просмотреть результаты объединений. - person Nicolas Renold; 19.06.2012