How can I add this sql request (cl_rank) to my request ?
My request (in class ClassementsTable) without the rank is:
$classement = $this->find('all')
->contain(['Users' => ['fields' => ['id', 'user_login', 'picture']]])
->select(['classement', 'points', 'participations'])
->order(["Classements.points" => "desc", "Classements.participations" => "asc"])
->disableHydration();
I need the cl_rank like this:
select user_id, points,
RANK() over (order by points desc)cl_rank
from classements;
newExpr could be a solution:
$query = $this->find('all');
$expr = $query->newExpr('RANK() over (order by points desc) cl_rank');
$classement = $query
->where(['user_id IN' => $equipe])
->contain(['Users' => ['fields' => ['id', 'user_login', 'picture']]])
->select(['classement', 'points', 'participations', $expr])
->order(["Classements.points" => "desc", "Classements.participations" => "asc"])
->disableHydration();
but some extra () are added around RANK() over (order by points desc) cl_rank and the sql request is not correct.
The generated sql request :
SELECT Classements.classement AS Classements__classement,
Classements.points AS Classements__points,
Classements.participations AS Classements__participations,
(RANK() over (order by points desc)cl_rank),
Users.id AS Users__id,
Users.user_login AS Users__user_login,
Users.picture AS Users__picture
FROM classements Classements INNER JOIN users Users ON Users.id = Classements.user_id
WHERE user_id in (SELECT DISTINCT EquipesUsers.user_id AS EquipesUsers__user_id
FROM equipes_users EquipesUsers WHERE equipe_id = :c0)
ORDER BY Classements.points desc, Classements.participations asc
This one is OK (without the ()):
SELECT Classements.classement AS Classements__classement,
Classements.points AS Classements__points,
Classements.participations AS Classements__participations,
RANK() over (order by points desc)cl_rank,
Users.id AS Users__id,
Users.user_login AS Users__user_login,
Users.picture AS Users__picture
FROM classements Classements
INNER JOIN users Users ON Users.id = Classements.user_id
WHERE user_id in (SELECT DISTINCT EquipesUsers.user_id AS EquipesUsers__user_id FROM equipes_users EquipesUsers WHERE equipe_id = :c0)
ORDER BY Classements.points desc, Classements.participations asc
Any idea to remove the ()
?
Window functions are supported out of the box (as of 4.1.0), they can be created via the functions builder.
There's shorthands for some window functions like for example lag()
, lead()
and rowNumber()
(maybe rank()
and others should've been among them), while others, and basically any arbitrary window function can be created via aggregate()
:
->select(function (\Cake\ORM\Query $query) {
return [
'classement',
'points',
'participations',
'cl_rank' => $query
->func()
->aggregate('RANK')
->order(['points' => 'DESC'])
->setReturnType('integer'),
];
})
Any aggregate function can be turned into a window function by either explicitly calling over()
(which will create an empty OVER ()
clause when no further clauses are added), or by calling one of partition()
, order()
, range()
, rows()
, groups()
, frame()
, excludeCurrent()
, excludeGroup()
or excludeTies()
, which will implicitly call over()
.
See also