如何限制每个logging/组包含的关联?

我有一个模型,文章,其中有许多摘要。 我想载入最新的10篇文章,并且对于每篇文章,最多的点数都是摘要。 我的function如下所示:

public function getArticles($category, $viewName) { $subArticles = $this->Articles->findByCategory($category)->contain([ 'Abstracts' => function ($q) { return $q ->select(['body', 'points', 'article_id']) ->where(['Abstracts.approved' => true]) ->limit(10) ->order(['Abstracts.points' => 'DESC']); } ]) ->limit(10) ->order(['Articles.created' => 'DESC']) ; $this->set( $viewName . 'Articles', $subArticles ); } 

我得到的结果不是我想要的。 通过SQL查看,首先CakePHP获取类别(罚款)中的所有items.id。 然后,CakePHP使用刚刚发现的10个articles.id文件进入摘要表,并要求获得最高票数的10个摘要(属于这些文章)。

问题是,我想要1篇摘要,而不是属于该类别的任何文章的10摘要。 我该如何解决这个问题? 谢谢!

编辑

ndmbuild议这是在包含模型上使用limit()的重复,所以我试图在那里的解决scheme。 也就是说,我把这个添加到我的模型中:

  $this->hasOne('TopAbstract', [ 'className' => 'Abstracts', 'foreignKey' => 'abstract_id', 'strategy' => 'select', 'sort' => ['TopAbstract.points' => 'DESC'], 'conditions' => function ($e, $query) { $query->limit(1); return $e; } ]); 

然后我尝试find文章的类别,包含(['TopAbstract']),只有这样会杀死我的SQL。 它死了一个可怕的死亡:

 Error: SQLSTATE[HY000]: General error: 1 near ")": syntax error 

debugging甚至不显示杀死它的查询,所以我不知道如何debugging这一个?

编辑

对自己说了一下,但错误肯定是在hasOne的“条件”部分。 我拿出来,它工作正常。 无法find这样的例子,应该看interwebs ..任何人有任何想法?

你正在寻找什么,是解决最大的每组问题。 你没有提到任何特定的RDBMS,但仍然可以参见http://dev.mysql.com/doc/refman/5.6/en/example-maximum-column-group-row.html

所以让我们来试一下,这里有三个选项可以应用在关联级别上,但是你可能会认为它们不是那么 “简单”的。


对于HasMany特定的东西,一直向下滚动!


select策略 – 在分组,最大值子查询上使用连接

 $this->hasOne('TopAbstracts', [ 'className' => 'Abstracts', 'strategy' => 'select', 'conditions' => function (\Cake\Database\Expression\QueryExpression $exp, \Cake\ORM\Query $query) { $query->innerJoin( [ 'AbstractsFilter' => $query ->connection() ->newQuery() ->select(['article_id', 'points' => $query->func()->max('points')]) ->from('abstracts') ->group('article_id') ], [ 'TopAbstracts.article_id = AbstractsFilter.article_id', 'TopAbstracts.points = AbstractsFilter.points' ] ); return []; } ]); 

这将通过基于最大点的连接查询select顶级摘要,它将看起来像

 SELECT TopAbstracts.id AS `TopAbstracts__id`, ... FROM abstracts TopAbstracts INNER JOIN ( SELECT article_id, (MAX(points)) AS `points` FROM abstracts GROUP BY article_id ) FilterAbstracts ON ( TopAbstracts.article_id = FilterAbstracts.article_id AND TopAbstracts.points = FilterAbstracts.points ) WHERE TopAbstracts.article_id in (1,2,3,4,5,6,7,8, ...) 

select策略 – 使用左自联接筛选

 $this->hasOne('TopAbstracts', [ 'className' => 'Abstracts', 'strategy' => 'select', 'conditions' => function (\Cake\Database\Expression\QueryExpression $exp, \Cake\ORM\Query $query) { $query->leftJoin( ['AbstractsFilter' => 'abstracts'], [ 'TopAbstracts.article_id = AbstractsFilter.article_id', 'TopAbstracts.points < AbstractsFilter.points' ]); return $exp->add(['AbstractsFilter.id IS NULL']); } ]); 

这将使用一个self-join基于没有a.points < b.points的行的filter,它会看起来像

 SELECT TopAbstracts.id AS `TopAbstracts__id`, ... FROM abstracts TopAbstracts LEFT JOIN abstracts FilterAbstracts ON ( TopAbstracts.article_id = FilterAbstracts.article_id AND TopAbstracts.points < FilterAbstracts.points ) WHERE (FilterAbstracts.id IS NULL AND TopAbstracts.article_id in (1,2,3,4,5,6,7,8, ...)) 

join策略 – 对连接条件使用子查询

 $this->hasOne('TopAbstracts', [ 'className' => 'Abstracts', 'foreignKey' => false, 'conditions' => function (\Cake\Database\Expression\QueryExpression $exp, \Cake\ORM\Query $query) { $subquery = $query ->connection() ->newQuery() ->select(['SubTopAbstracts.id']) ->from(['SubTopAbstracts' => 'abstracts']) ->where(['Articles.id = SubTopAbstracts.article_id']) ->order(['SubTopAbstracts.points' => 'DESC']) ->limit(1); return $exp->add(['TopAbstracts.id' => $subquery]); } ]); 

这将使用一个相关的子查询,使用一个相当具体的select与简单的sorting和限制select顶级评论。 请注意,为了避免将另外的Articles.id = TopAbstracts.article_id条件编译到连接条件中, foreignKey选项设置为false

查询将看起来像

 SELECT Articles.id AS `Articles__id`, ... , TopAbstracts.id AS `TopAbstracts__id`, ... FROM articles Articles LEFT JOIN abstracts TopAbstracts ON ( TopAbstracts.id = ( SELECT SubTopAbstracts.id FROM abstracts SubTopAbstracts WHERE Articles.id = SubTopAbstracts.article_id ORDER BY SubTopAbstracts.points DESC LIMIT 1 ) ) 

所有这3个选项都将查询并注入logging,而不会有任何的嘲弄,这只是不太“直截了当”。


手动方法

为了完整起见,当然总是可以手动加载关联的logging并适当地格式化结果,例如使用结果格式化程序,例如,参见CakePHP实体包含没有外键


select策略和颠倒的顺序

仅供参考,我最初偶然发现的奇怪解决scheme之一。 这一个真的不应该使用!

这将select所有相关的摘要,然后ORM将遍历它们,并为每篇文章select第一个匹配的article_id值。 所以从理论上讲,当要求points ,ORM应该select最多的分数。

虽然我本来预料到这是开箱即用的,但似乎ORM会以相反的顺序迭代结果,这会导致错误的行被选中。 为了得到这个工作,查询需要使用通常需要使用的相反顺序,即ASC而不是DESC

 $this->hasOne('TopAbstracts', [ 'className' => 'Abstracts', 'foreignKey' => 'abstract_id', 'strategy' => 'select', 'conditions' => function (\Cake\Database\Expression\QueryExpression $exp, \Cake\ORM\Query $query) { $query->order(['TopAbstracts.points' => 'ASC']); return []; } ]); 

此外,该函数需要返回一个空数组而不是如链接答案中所示的expression式,因为这会导致无效的SQL被编译。 这两种行为,颠倒的顺序迭代和无效的SQL可能都是bug。

虽然这会起作用,但它会一直select所有相关的摘要,而不仅仅是顶级的,这可能被认为效率很低,而且看起来像

 SELECT Articles.id AS `Articles__id`, ... FROM articles Articles 
 SELECT TopAbstracts.id AS `TopAbstracts__id`, ... FROM abstracts TopAbstracts WHERE TopAbstracts.article_id in (1,2,3,4,5,6,7,8, ...) ORDER BY TopAbstracts.points ASC 

HasMany关联

我给了HasMany协会一个尝试,但我现在太忙了,进一步追求…只是为了testing目的,基于ROW_NUMBER()模拟与MySQL相似, 为每个人select顶部Xlogging在桌子里

如果有人感兴趣,请查看https://gist.github.com/ndm2/039da4009df1c5bf1c262583603f8298