问题
SELECT invoice.id,
COUNT(slip.id),
SUM(projected_minutes) OVER (PARTITION BY task.id) AS projected_minutes
FROM invoice
INNER JOIN task ON task.invoice_id = invoice.id
LEFT JOIN slip ON slip.task_id = task.id
The query above is in postgresql, and I want to convert it to DQL, but I cant find any documentation for window functions in DQL, is this natively supported in doctrine or would i have to create a custom dql function for this?
回答1:
There is no support for this vendor specific function in Doctrine. Either create a custom DQL function or use Native SQL.
回答2:
As asked in one of the comments, I attach my custom string function for COUNT() OVER()
<?php
namespace Example\Doctrine\CustomFunctions;
use Doctrine\ORM\Query\AST\Functions\FunctionNode;
use Doctrine\ORM\Query\Lexer;
/**
* Class CountOverSql
*/
class CountOverSql extends FunctionNode
{
/**
* @var string
*/
private $field;
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
{
return "COUNT(".$this->field->dispatch($sqlWalker).") OVER()";
}
public function parse(\Doctrine\ORM\Query\Parser $parser): void
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$this->field = $parser->StringPrimary();
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
}
}
and you only need to add this code to your doctrine configuration:
$entityManager->getConfiguration()->addCustomStringFunction('count_over', function ($name) use ($c) {
return new Example\Doctrine\CustomFunctions\CountOverSql($name);
});
And to use it, add this code to your select statement:
$queryBuilder->select('table_name', 'count_over(table_name.id)');
Hope it helps someone.
回答3:
Slightly different version of window function class. It's more universal, because you can use here any aggregate function + use "Order By" clause in window.
use Doctrine\ORM\Query\AST\AggregateExpression;
use Doctrine\ORM\Query\AST\Functions\FunctionNode;
use Doctrine\ORM\Query\AST\OrderByClause;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
class Over extends FunctionNode
{
/** @var AggregateExpression */
private $functionExpr;
/** @var OrderByClause */
private $orderByExpr;
/**
* {@inheritdoc}
*
* @throws \Doctrine\ORM\Query\AST\ASTException
*/
public function getSql(SqlWalker $sqlWalker): string
{
if (!empty($this->orderByExpr->orderByItems)) {
return "{$this->functionExpr->dispatch($sqlWalker)} OVER ({$this->orderByExpr->dispatch($sqlWalker)})";
}
return "{$this->functionExpr->dispatch($sqlWalker)} OVER ()";
}
/**
* @param Parser $parser
*
* @throws \Doctrine\ORM\Query\QueryException
*/
public function parse(Parser $parser): void
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$this->functionExpr = $parser->StringPrimary();
$parser->match(Lexer::T_COMMA);
$this->orderByExpr = $parser->OrderByClause();
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
}
}
And use it like this:
$aggregateQueryResult = $this->createQueryBuilder('agg')
->select('OVER(sum(agg.quantity), ORDER BY agg.date, agg.id) as aggregated_sum')
来源:https://stackoverflow.com/questions/15646912/can-i-use-window-functions-in-doctrine-2