我试图创建一个使用案例返回列总和的查询(它记录时间和格式的分钟或小时,如果它以小时为单位,乘以60到转换为分钟)。我非常接近,但是查询没有填充ELSE
的{{1}}部分。
取景器方法是:
CASE
生成的查询是:
public function findWithTotalTime(Query $query, array $options)
{
$conversionCase = $query->newExpr()
->addCase(
$query->newExpr()->add(['Times.time' => 'hours']),
['Times.time*60', 'Times.time'],
['integer', 'integer']
);
return $query->join([
'table' => 'times',
'alias' => 'Times',
'type' => 'LEFT',
'conditions' => 'Times.category_id = Categories.id'
])->select([
'Categories.name',
'total' => $query->func()->sum($conversionCase)
])->group('Categories.name');
}
它在CASE结束之前缺少ELSE语句,根据API文档:
SELECT Categories.name AS `Categories__name`, (SUM((CASE WHEN
Times.time = :c0 THEN :c1 END))) AS `total` FROM categories Categories
LEFT JOIN times Times ON Times.category_id = Categories.id GROUP BY
Categories.name
https://api.cakephp.org/3.3/class-Cake.Database.Expression.QueryExpression.html
我知道可能有更好的方法来做到这一点,但此时我想至少知道如何使用内置的QueryBuilder正确地做CASE语句。
答案 0 :(得分:1)
看起来Cookbook中存在一些文档问题,API也可能在这个问题上更加清晰。 $conditions
参数和$values
参数都必须是数组才能使其正常工作。
此外,您传递的SQL表达式错误,包括错误的类型,将类型定义为integer
将导致$values
中传递的数据被转换为这些类型,这意味着你将留下0
s。
在处理需要安全传递的用户输入时,您使用的语法非常有用。但是,在您的情况下,您希望传递硬编码标识符,因此您需要使用key => value
语法将值作为文字或标识符传递。这看起来像是:
'Times.time' => 'identifier'
然而,遗憾的是,似乎存在一个错误(或至少是一个未记录的限制),导致else部分无法正确识别此语法,因此现在您必须使用手动方式,即通过正确的表达式对象,顺便说一句,你可能应该为Times.time*60
做过,因为如果正在应用/需要自动标识符引用,它将会破坏。
以下是所有前述技术的完整示例:
use Cake\Database\Expression\IdentifierExpression;
// ...
$conversionCase = $query
->newExpr()
->addCase(
[
$query->newExpr()->add(['Times.time' => 'hours'])
],
[
$query
->newExpr(new IdentifierExpression('Times.time'))
->add('60')
->tieWith('*'), // setConjunction() as of 3.4.0
new IdentifierExpression('Times.time')
],
);
如果您确定自己从未使用自动标识符引用,那么您可以将乘法片段传递为:
'Times.time * 60' => 'literal'
或:
$query->newExpr('Times.time * 60')