2

Tables:

people(id, name)
job (id, people_id, job_title, salary) 

Goal: Display each unique job, the total average salary (FLOAT and rounded to 2 decimal places), the total people and the total salary (Float and rounded to 2 decimal places) and order by highest average salary.

So the challenge is to keep the cast type as float while rounding it to 2 decimal places.

I've gotten to where I've rounded it 2 decimal places but it's not float. I've gotten it to where it's float but I can't round it to 2 decimal places.

My Attempts:

Attempt 1:

SELECT 
  distinct(j.job_title) as job_title,
  to_char(AVG(j.salary)::FLOAT, 'FM999999990.00') as average_salary,
  COUNT(p.id) as total_people,
  CAST (SUM(j.salary) AS FLOAT) as total_salary
  FROM people p
    JOIN job j on p.id = j.people_id
  GROUP BY j.job_title
  ORDER BY total_salary

Problem: Still says it's not float

Attempt 2:

SELECT 
  distinct(j.job_title) as job_title,
  CAST (AVG(j.salary) AS FLOAT) as average_salary,
  COUNT(p.id) as total_people,
  CAST (SUM(j.salary) AS FLOAT) as total_salary
  FROM people p
    JOIN job j on p.id = j.people_id
  GROUP BY j.job_title
  ORDER BY total_salary

Problem: not rounded to 2 decimal places

Attempt 3:

SELECT 
  distinct(j.job_title) as job_title,
  ROUND (AVG(CAST(j.salary as FLOAT)), 2)) as average_salary,
  COUNT(p.id),
  ROUND (SUM(CAST(j.salary as FLOAT)), 2)) as total_salary
  FROM people p
    JOIN job j on p.id = j.people_id
  GROUP BY j.job_title
  ORDER BY total_salary

I get an error saying I need to add explicit cast types which led me to attempt number 1.

GMB
  • 216,147
  • 25
  • 84
  • 135
Maggie Liu
  • 344
  • 1
  • 3
  • 15

2 Answers2

4

The answer depends on the actual datatype of column salary. The key point is that round() in Postgres does not allows floats (only numeric types are supported).

If you are dealing with a numeric datatype, then you can first round(), then cast to float:

round(avg(salary), 2)::float

If you are dealing with a float column, then you would need to cast the result of the aggregate function before using round() on it:

round(avg(salary)::numeric, 2)::float
GMB
  • 216,147
  • 25
  • 84
  • 135
1

So the challenge is to keep the cast type as float while rounding it to 2 decimal places.

If you strictly want to avoid casting away from float, you could do it like this:

test=# 
test=# SELECT float '12.3456'
test-#      , round(float '12.3456' * 100) / 100 AS rounded
 float8  | rounded
---------+----------
 12.3456 |    12.35

The point being: round() taking the number of decimal places as 2nd parameter is based on numeric (since floating point numbers are imprecise by nature).
But there is an overloaded variant of round() taking a single parameter that rounds to the nearest integer. Multiply by 100 before and divide by 100 after.

Or overcome your aversion against numeric and use round(numeric, int) as provided by GMB.

Query

SELECT j.job_title  -- !
     , round(AVG(j.salary) * float '100') / 100 AS average_salary  -- !
     , COUNT(p.id) AS total_people
     , SUM(j.salary)::float AS total_salary  -- short cast syntax
FROM   people p
JOIN   job    j ON p.id = j.people_id
GROUP  BY j.job_title
ORDER  BY total_salary DESC NULLS LAST -- ! "order by highest average salary" 
  • Multiplying by * float '100' achieves the cast and the multiplication in one step - because the data type resulting from avg() depends on the input, quoting the manual:

    numeric for any integer-type argument, double precision for a floating-point argument, otherwise the same as the argument data type

    About casting in Postgres:

  • distinct(j.job_title) is most probably not doing what you may think it does. There is no function distinct(). DISTINCT is a syntax element - and completely useless here, only adding cost. See this related case with explanation:

  • About NULLS LAST:

Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228