如何限制每个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