问题
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.
回答1:
The answer depends on the actual datatype of column salary
. The key point is that round()
in Postgres does not allows float
s (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
回答2:
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 fromavg()
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 typeAbout casting in Postgres:
- Postgres data type cast
distinct(j.job_title)
is most probably not doing what you may think it does. There is no functiondistinct()
.DISTINCT
is a syntax element - and completely useless here, only adding cost. See this related case with explanation:- Skip whole row if aggregated value is null
About
NULLS LAST
:- PostgreSQL sort by datetime asc, null first?
来源:https://stackoverflow.com/questions/55426801/changing-data-type-to-float-and-rounding-to-2-decimal-digits