Условие
Есть таблица reviews (review_id, enroll_id, rating INT 1–5, review_date).
Напишите запрос, который выведет среднюю оценку по всем отзывам, округлённую до 2 знаков после запятой.
PostgreSQL 15.5.
Решение
Подход
AVG(rating) посчитает среднее. PostgreSQL вернёт numeric (если rating — INTEGER, среднее уже будет numeric с большим числом знаков). Применяем ROUND(..., 2).
Реализация
SELECT ROUND(AVG(rating)::numeric, 2) AS avg_rating
FROM reviews;Почему ::numeric
ROUND(double precision, int) в PostgreSQL не существует в этой сигнатуре — есть ROUND(numeric, int) (с точностью) и ROUND(double precision) (без). Поэтому нужно явно привести AVG к numeric.
-- Это упадёт с ошибкой: function round(double precision, integer) does not exist
SELECT ROUND(AVG(rating), 2) FROM reviews;
-- Правильно:
SELECT ROUND(AVG(rating)::numeric, 2) FROM reviews;
-- Альтернатива:
SELECT ROUND(CAST(AVG(rating) AS numeric), 2) FROM reviews;Сложность
O(n) — один проход по таблице. Индекс не нужен.
Подводные камни
ROUND(AVG(rating), 2)без cast. В PostgreSQL даст ошибку. В MySQL/SQL Server — сработает, но запрос не будет переносим.AVGигнорируетNULL. Если вratingестьNULL-ы (не должно быть по схеме, но мало ли), они выпадут из числителя и знаменателя. Чтобы засчитатьNULLкак 0:AVG(COALESCE(rating, 0)).- Деление целых.
SUM(rating) / COUNT(*)— если обаINTEGER, получите целочисленное деление (12 / 5 = 2, а не2.4).AVGсразу возвращаетnumeric/double. - Округление банкира. PostgreSQL
ROUNDдляnumericиспользует «half away from zero» (стандартное), дляdouble precision— «half to even». В этой задаче дляrating 1–5различий не будет.
Эталонный ответ
SELECT ROUND(AVG(rating)::numeric, 2) AS avg_rating
FROM reviews;