Reputation: 17
I created a view that have 3 columns: service_id, professional_id and avg(rating), and this view is working fine.
CREATE VIEW nota_profissionais
AS
SELECT
service_id, professional_id, avg(nota) AS nota_media
FROM
avaliacao, servico_executado, solicitacao
WHERE
avaliacao.service_id = servico_executado.id
AND servico_executado.id_solicitacao = solicitacao.id
GROUP BY
service_id, professional_id;
Now, for each service_id
it returns (each type of service), I need to show the professional which has the best average rating. I tried doing this:
SELECT
service_id, professional_id, MAX(nota_media) AS nota_media
FROM
nota_profissionais
GROUP BY
service_id, professional_id;
But it returns the exact same thing from the view, showing every professional_id per service_id. I know why that's happening, but don't know to how to do this query correctly. Sorry if I'm not being clear, as English isn't my native language.
Upvotes: 0
Views: 37
Reputation: 15624
If there are several professionals with the same best rating and you want to get all of them, not only the first random:
select t.*
from nota_profissionais t
join (
select service_id, max(nota_media) as nota_media
from nota_profissionais
group by service_id) t1
on (t.service_id = t1.service_id and t.nota_media = t1.nota_media)
Actually the same result should return the @Hogan's answer if change the ROW_NUMBER()
to the RANK()
function.
Upvotes: 0
Reputation: 1270081
I think the best approach is DISTINCT ON
:
SELECT DISTINCT ON (service_id) np.*
FROM nota_profissionais np
ORDER BY service_id, nota_media DESC;
Not only is the shortest in query length, but DISTINCT ON
often has better performance in comparison to other methods.
Upvotes: 0
Reputation: 23870
You can do it with a LATERAL
subquery:
SELECT *
FROM
(SELECT DISTINCT service_id FROM nota_profissionais) t1,
LATERAL (SELECT professional_id, nota_media
FROM nota_profissionais
WHERE service_id=t1.service_id
ORDER BY nota_media DESC LIMIT 1) t2
This will find all distinct service_id
s (first subquery) and then for each one of them, it will find a the best professional (second subquery, the one with LATERAL
).
If you have a lot of data, make sure that you have an index on (service_id, nota_media DESC)
.
Upvotes: 0
Reputation: 70523
You can use a windowing function to do this:
SELECT service_id, professional_id, nota_media,
FROM (
SELECT service_id, professional_id, nota_media,
ROW_NUMBER() OVER (PARTITION BY service_id ORDER BY nota_media DESC) as RN
) x
WHERE RN = 1
Upvotes: 1