Question
I am using PostgreSQL through the Ruby sequel gem and want to round an average to two decimal places.
This query causes an error:
SELECT ROUND(AVG(some_column), 2)
FROM table_name;
The error is:
PG::Error: ERROR: function round(double precision, integer) does not exist
(Sequel::DatabaseError)
However, this query works without error:
SELECT ROUND(AVG(some_column))
FROM table_name;
What is causing this error, and how can I correctly round the average to 2 decimal places in PostgreSQL?
Short Answer
By the end of this page, you will understand why ROUND(AVG(column), 2) sometimes fails in PostgreSQL, how PostgreSQL handles double precision vs numeric, and how to fix the problem by casting values to the correct type before rounding.
Concept
PostgreSQL has different numeric data types, and function behavior depends on the type of value you pass in.
In this case, the important idea is:
AVG(...)does not always return the same type.ROUND(...)has different supported signatures depending on the type.
A common cause of this error is that AVG(some_column) is returning a double precision value, while ROUND(value, 2) in PostgreSQL expects a numeric value for the two-argument version.
Why the error happens
PostgreSQL supports:
ROUND(double precision)
but the two-argument version is for numeric:
ROUND(numeric, integer)
So this works:
SELECT ROUND(AVG(some_column))
FROM table_name;
because PostgreSQL can apply the one-argument ROUND to a floating-point result.
Mental Model
Think of PostgreSQL functions like tools with specific socket sizes.
ROUND(value)is a tool that fits one kind of input.ROUND(value, 2)is a more specialized tool.- If you hand PostgreSQL a
double precisionvalue when it expectsnumeric, the tool does not fit.
Casting is like putting an adapter on the value so it fits the correct function.
So instead of saying:
ROUND(AVG(some_column), 2)
you say:
ROUND(AVG(some_column)::numeric, 2)
Now PostgreSQL sees a numeric value and can use the correct version of ROUND.
Syntax and Examples
The usual fix is to cast the average to numeric before rounding.
SELECT ROUND(AVG(some_column)::numeric, 2)
FROM table_name;
You can also use CAST(...):
SELECT ROUND(CAST(AVG(some_column) AS numeric), 2)
FROM table_name;
Example
Suppose you have values like this:
SELECT AVG(score) AS avg_score
FROM results;
If AVG(score) returns something like 87.6666666667, you can round it to two decimal places:
SELECT ROUND(AVG(score)::numeric, 2) AS avg_score
FROM results;
Step by Step Execution
Consider this query:
SELECT ROUND(AVG(price)::numeric, 2) AS average_price
FROM products;
Step by step:
- PostgreSQL reads values from the
pricecolumn. AVG(price)calculates the mean value.- The result is cast to
numericusing::numeric. ROUND(..., 2)rounds that numeric result to 2 decimal places.- The final rounded value is returned as
average_price.
Small trace example
Imagine the price values are:
10.10
10.20
10.25
The average is:
(10.10 + 10.20 + 10.25) / 3 = 10.183333...
After casting and rounding:
SELECT ROUND(10.183333::numeric, );
Real World Use Cases
Rounding averages to fixed decimal places is common in many kinds of software.
Reporting dashboards
You may show average order value, average session duration, or average review score:
SELECT ROUND(AVG(order_total)::numeric, 2)
FROM orders;
Finance and billing
Money values usually need exact decimal handling:
SELECT ROUND(AVG(invoice_amount)::numeric, 2)
FROM invoices;
Education platforms
Average exam scores are often shown with one or two decimal places:
SELECT ROUND(AVG(mark)::numeric, 2)
FROM exam_results;
APIs
An API response may return rounded aggregates for frontend display:
SELECT ROUND(AVG(response_time_ms)::numeric, 2) AS avg_response_time
FROM request_logs;
Real Codebase Usage
In real projects, developers usually combine rounding with a few common patterns.
1. Aliasing the result
Give the rounded value a clear column name:
SELECT ROUND(AVG(score)::numeric, 2) AS average_score
FROM results;
2. Handling NULL values
If there are no rows, AVG(...) returns NULL:
SELECT COALESCE(ROUND(AVG(score)::numeric, 2), 0) AS average_score
FROM results;
3. Filtering before averaging
Often averages only apply to valid rows:
SELECT ROUND(AVG(score)::numeric, 2) AS average_score
FROM results
WHERE score IS NOT NULL;
4. Grouped reports
Common Mistakes
Mistake 1: Using ROUND(value, 2) on double precision
Broken example:
SELECT ROUND(AVG(some_column), 2)
FROM table_name;
Why it fails:
AVG(some_column)may bedouble precision- PostgreSQL does not support
round(double precision, integer)
Fix:
SELECT ROUND(AVG(some_column)::numeric, 2)
FROM table_name;
Mistake 2: Confusing storage type with display format
Rounding affects the numeric result, but some client tools may still display values in their own format.
Example:
SELECT ROUND(AVG(score)::numeric, 2)
FROM results;
This rounds the value, but how it appears in an app may depend on Ruby, Sequel, or the frontend.
Mistake 3: Rounding too early
Comparisons
| Concept | Best for | Example | Notes |
|---|---|---|---|
ROUND(value) | Rounding to a whole number | ROUND(AVG(score)) | Works with double precision |
ROUND(value, 2) | Rounding to fixed decimal places | ROUND(AVG(score)::numeric, 2) | Usually needs numeric |
double precision | Fast approximate floating-point math | scientific values, measurements | Not ideal for exact decimal formatting |
numeric | Exact decimal precision | prices, money, reports |
Cheat Sheet
Fix for the error
SELECT ROUND(AVG(some_column)::numeric, 2)
FROM table_name;
Equivalent cast syntax
SELECT ROUND(CAST(AVG(some_column) AS numeric), 2)
FROM table_name;
Key rule
ROUND(double precision)existsROUND(numeric, integer)existsROUND(double precision, integer)does not exist
Common patterns
-- Basic average rounded to 2 decimals
SELECT ROUND(AVG(score)::numeric, 2) FROM results;
-- With alias
SELECT ROUND(AVG(score)::numeric, 2) AS average_score FROM results;
(ROUND((score)::, ), ) results;
category_id, ROUND((price)::, )
products
category_id;
FAQ
Why does ROUND(AVG(column), 2) fail in PostgreSQL?
Because the result of AVG(column) may be double precision, and PostgreSQL does not provide a round(double precision, integer) function.
How do I round to 2 decimal places in PostgreSQL?
Cast the value to numeric first:
SELECT ROUND(AVG(column)::numeric, 2)
FROM table_name;
Why does ROUND(AVG(column)) work without a cast?
Because PostgreSQL supports the one-argument form for floating-point values.
Should I use numeric or double precision for money values?
Use numeric for money and exact decimal calculations.
Does this problem come from Ruby Sequel?
No. The error comes from PostgreSQL function/type rules. Sequel is just showing the database error.
Can I use CAST(... AS numeric) instead of ?
Mini Project
Description
Build a small reporting query for an online store that calculates average product prices per category and rounds them to two decimal places. This demonstrates the exact issue from the question: aggregate results may need casting before PostgreSQL can round them to a fixed number of decimal places.
Goal
Create a query that returns each category's average product price rounded to 2 decimal places.
Requirements
- Create a
productstable with a category name and price column. - Insert a few sample rows with decimal prices.
- Write a query that calculates the average price for each category.
- Round each average to 2 decimal places.
- Return the rounded value with a clear alias.
Keep learning
Related questions
Calling a Class Method from an Instance in Ruby
Learn how to call a class method from an instance in Ruby using self.class, with examples, pitfalls, and practical usage patterns.
Calling an Overridden Monkey-Patched Method in Ruby
Learn how to call the original method when monkey patching in Ruby, including alias_method patterns, examples, pitfalls, and practical usage.
Convert a Unix Timestamp to Ruby DateTime
Learn how to convert Unix timestamps to Ruby DateTime and Time objects, with examples, differences, pitfalls, and practical Ruby usage.