Порядок внутри группы в Doctrine 2

Я использую Symfony 2 PR12 с Doctrine 2 и MySQL. У меня есть база данных, в которой хранятся статьи и просмотры этих статей:

// ...
class Article {

    /**
     * @orm:Column(type="bigint")
     * @orm:Id
     * @orm:GeneratedValue
     * @var int
     */
    protected $id;

    /**
     * @orm:OneToMany(targetEntity="ArticleView",mappedBy="article")
     * @var ArrayCollection
     */
    protected $views;

    // ...
}

// ...
class ArticleView {

    /**
     * @orm:Column(type="bigint")
     * @orm:Id
     * @orm:GeneratedValue
     * @var int
     */
    protected $id;

    /**
     * @orm:Column(type="bigint",name="DateRead",nullable=true)
     * @var int
     */
    protected $viewDate;

    /**
     * @orm:ManyToOne(targetEntity="Article",inversedBy="views")
     * @var Article
     */
    protected $article;

    // ...
}

Я хочу получить, например, 20 последних просмотренных статей. Моя первая мысль будет примерно такой:

$qb = <instance of Doctrine\ORM\QueryBuilder>;
$qb->select('a')
   ->from('Article', 'a')
   ->join('a.views', 'v')
   ->orderBy('v.viewDate', 'DESC')
   ->groupBy('a.id')
   ->setMaxResults(20)
;

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

Это ожидаемое поведение для MySQL, так как группировка обрабатывается перед упорядочением, и есть работающие решения этой проблемы с необработанными запросами по адресу http://www.artfulsoftware.com/infotree/mysqlquerytree.php (Агрегаты -> Агрегаты внутри группы). Но я не могу понять, как перевести любое из этих решений в DQL, поскольку, насколько я могу судить, нет возможности выбирать из подзапросов или выполнять соединения с самоисключением.

Любые идеи о том, как решить проблему с разумной производительностью?


person Kevin Montag    schedule 23.04.2011    source источник
comment
Я не думаю, что тип столбца должен быть bigint, вместо этого должен быть обычный integer.   -  person Cobby    schedule 24.04.2011


Ответы (1)


В итоге я решил это с помощью коррелированного подзапроса:

$qb
    ->select('a')
    ->from('Article', 'a')
    ->join('a.views', 'v')
    ->orderBy('v.viewDate', 'DESC')
    ->setMaxResults(20)

    // Only select the most recent article view for each individual article
    ->where('v.viewDate = (SELECT MAX(v2.viewDate) FROM ArticleView v2 WHERE v2.article = a)')

Таким образом, сортировка игнорирует ArticleView, кроме самого последнего для любой данной статьи. Хотя я предполагаю, что это работает довольно плохо по сравнению с другими необработанными решениями SQL - любые ответы с более высокой производительностью все равно будут высоко оценены :).

person Kevin Montag    schedule 29.04.2011
comment
Вы дважды пробовали присоединять просмотры к статьям?.. ->join('a.views', 'v')->leftJoin('a.views', 'v2', Expr\Join::WITH, 'v.viewDate < v2.viewDate)->where('v2.id IS NULL'). Однако это не самосоединение, поэтому делать это может быть неразумно, если левое соединение со статьями не возвращает те же результаты, что и первое соединение. - person dianovich; 24.06.2012