我想在CakePHP 3.0.0中对联合查询进行分页。通过使用custom finder,我可以完美地使用 ,但我找不到任何方法让limit
和offset
应用于联合,而不是任何一个子查询。
换句话说,这段代码:
$articlesQuery = $articles->find('all');
$commentsQuery = $comments->find('all');
$unionQuery = $articlesQuery->unionAll($commentsQuery);
$unionQuery->limit(7)->offset(7); // nevermind the weirdness of applying this manually
生成此查询:
(SELECT {article stuff} ORDER BY created DESC LIMIT 7 OFFSET 7)
UNION ALL
(SELECT {comment stuff})
而不是我想要的,这就是:
(SELECT {article stuff})
UNION ALL
(SELECT {comment stuff})
ORDER BY created DESC LIMIT 7 OFFSET 7
我可以手动构建正确的查询字符串,如下所示:
$unionQuery = $articlesQuery->unionAll($commentsQuery);
$sql = $unionQuery->sql();
$sql = "($sql) ORDER BY created DESC LIMIT 7 OFFSET 7";
但是我的自定义finder方法需要返回一个\Cake\Database\Query
对象,而不是一个字符串。
所以,
limit()
等方法应用于整个联合查询?注意:
a closed issue描述了与此类似的内容(使用paginate($unionQuery)
除外),但没有提出如何解决问题的建议。
limit
设置为5,则完整结果集为:
Article 9 --|
Article 8 |
Article 7 -- Page One
Article 6 |
Article 5 --|
Article 4 --|
Comment 123 |
Article 3 -- Here be dragons
Comment 122 |
Comment 121 --|
...
然后第1页的查询将起作用,因为(前五篇文章)+(前五项评论),按日期手动排序,并修剪为合并结果的前五项,将导致文章1-5
但第2页无法正常使用,因为offset
5将适用于文章和评论,这意味着前5条评论(未包含在第1页中) ),永远不会出现在结果中。
答案 0 :(得分:5)
能够在unionAll()
返回的查询中直接应用 这些子句是不可能的AFAIK,它需要更改API才能使编译器知道SQL的放置位置,通过选项,一种新类型的查询对象,无论如何。
幸运的是,可以使用Query::epilog()
将SQL附加到查询中,因为它是原始SQL片段
$unionQuery->epilog('ORDER BY created DESC LIMIT 7 OFFSET 7');
或查询表达式
$unionQuery->epilog(
$connection->newQuery()->order(['created' => 'DESC'])->limit(7)->offset(7)
);
这可以为您提供所需的查询。
应该注意的是,根据文档Query::epilog()
要求在\Cake\Database\ExpressionInterface
实例的形式中使用字符串或具体\Cake\Database\Expression\QueryExpression
实现,而不仅仅是 any ExpressionInterface
实现,理论上后一个例子是无效的,即使查询编译器适用于任何ExpressionInterface
实现。
也可以将联合查询用作子查询,这样可以在使用分页组件的情况下使事情变得更容易,因为您不必处理除了构建和之外的任何事情。注入子查询,因为paginator组件只能在主查询上应用order / limit / offset。
/* @var $connection \Cake\Database\Connection */
$connection = $articles->connection();
$articlesQuery = $connection
->newQuery()
->select(['*'])
->from('articles');
$commentsQuery = $connection
->newQuery()
->select(['*'])
->from('comments');
$unionQuery = $articlesQuery->unionAll($commentsQuery);
$paginatableQuery = $articles
->find()
->from([$articles->alias() => $unionQuery]);
这当然也可以移到查找器中。