如何修改CakePHP 3中的UNION查询?

Pha*_*son 7 cakephp cakephp-3.0

我想在CakePHP 3.0.0中对联合查询进行分页.通过使用自定义查找器,我使它几乎完美地工作,但我找不到任何方式来获取limitoffset应用于联合,而不是任何子查询.

换句话说,这段代码:

$articlesQuery = $articles->find('all');
$commentsQuery = $comments->find('all');
$unionQuery = $articlesQuery->unionAll($commentsQuery);
$unionQuery->limit(7)->offset(7); // nevermind the weirdness of applying this manually
Run Code Online (Sandbox Code Playgroud)

产生这个查询:

(SELECT {article stuff} ORDER BY created DESC LIMIT 7 OFFSET 7)
UNION ALL 
(SELECT {comment stuff}) 
Run Code Online (Sandbox Code Playgroud)

而不是我想要的,这是:

(SELECT {article stuff})
UNION ALL 
(SELECT {comment stuff})
ORDER BY created DESC LIMIT 7 OFFSET 7
Run Code Online (Sandbox Code Playgroud)

可以像这样手动构造正确的查询字符串:

$unionQuery = $articlesQuery->unionAll($commentsQuery);
$sql = $unionQuery->sql();
$sql = "($sql) ORDER BY created DESC LIMIT 7 OFFSET 7";
Run Code Online (Sandbox Code Playgroud)

但我的自定义finder方法需要返回一个\Cake\Database\Query对象,而不是一个字符串.

所以,

  • 有没有办法将方法应用于limit()整个联合查询?
  • 如果没有,有没有办法将SQL查询字符串转换为Query对象?

注意:有一个封闭的问题描述了与此类似的东西(使用除外paginate($unionQuery)),但没有提出如何克服这个问题的建议.

对每个子查询应用限制和偏移量?

scrowler友好地建议了这个选项,但我认为它不会起作用.如果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   --|
...
Run Code Online (Sandbox Code Playgroud)

然后第1页的查询将起作用,因为(前五篇文章)+(前五项注释),按日期手动排序,并修剪为组合结果的前五项将产生第1-5条.

但第2页不起作用,因为offset5将适用于文章和评论,这意味着前5条评论(未包含在第1页中)将永远不会出现在结果中.

ndm*_*ndm 5

能够直接在返回的查询上应用这些子句unionAll()是不可能的AFAIK,它需要更改API,使编译器知道在哪里放置SQL,通过选项,新类型的查询对象,等等.

查询:: epilog()来救援

幸运的是,可以将SQL附加到查询中,使用Query::epilog()它作为原始SQL片段

$unionQuery->epilog('ORDER BY created DESC LIMIT 7 OFFSET 7');
Run Code Online (Sandbox Code Playgroud)

或查询表达式

$unionQuery->epilog(
    $connection->newQuery()->order(['created' => 'DESC'])->limit(7)->offset(7)
);
Run Code Online (Sandbox Code Playgroud)

这应该为您提供所需的查询.

应该注意的是,根据文档要求Query::epilog()字符串或实例\Cake\Database\ExpressionInterface形式的具体实现\Cake\Database\Expression\QueryExpression,而不仅仅是任何 ExpressionInterface实现,所以理论上后一个例子是无效的,即使查询编译器适用于任何ExpressionInterface实现.

使用子查询

也可以将union查询用作子查询,这样可以在使用分页组件的环境中更容易,因为除了构建和注入子查询之外,您不必处理任何其他事情,因为paginator组件会能够简单地在主查询上应用顺序/限制/偏移.

/* @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]);
Run Code Online (Sandbox Code Playgroud)

这当然也可以移到查找器中.