2016-12-18 3 views
3

Я пытаюсь решить эту конкретную проблему с PGExercises.com:Округление числа до ближайшего 10 в Postgres

https://www.pgexercises.com/questions/aggregates/rankmembers.html

Суть вопроса в том, что я дал таблица членов клуба и полчаса временных интервалов, которые они забронировали (получение списка - простая ВХОДНАЯ ВСТУПЛЕНИЕ из двух таблиц).

Я должен произвести нисходящее ранжирование участников по общего количества часов бронировали, округляется до ближайшего . Мне также нужно создать столбец с рангом, используя оконную функцию RANK() и отсортировать результат по рангу. (Результат производит 30 записей.)

очень элегантное решение автора заключается в следующем:

select firstname, surname, hours, rank() over (order by hours) from 
(select firstname, surname, 
((sum(bks.slots)+5)/20)*10 as hours 

from cd.bookings bks 
inner join cd.members mems 
    on bks.memid = mems.memid 
group by mems.memid 
) as subq 
order by rank, surname, firstname; 

К сожалению, как SQL новичок, мое очень unelegant решение гораздо более запутанным, используя CASE WHEN и преобразование чисел в текст для того, чтобы посмотреть на последнюю цифру для принятия решения о том, чтобы округлить до или вниз:

SELECT 
firstname, 
surname, 
CASE 
    WHEN (SUBSTRING(ROUND(SUM(slots*0.5),0)::text from '.{1}$') IN ('5','6','7','8','9','0')) THEN CEIL(SUM(slots*0.5) /10) * 10 
    ELSE FLOOR(SUM(slots*0.5) /10) * 10 
END AS hours, 
RANK() OVER(ORDER BY CASE 
    WHEN (SUBSTRING(ROUND(SUM(slots*0.5),0)::text from '.{1}$') IN ('5','6','7','8','9','0')) THEN CEIL(SUM(slots*0.5) /10) * 10 
    ELSE FLOOR(SUM(slots*0.5) /10) * 10 
END DESC) as rank 
FROM cd.bookings JOIN cd.members 
ON cd.bookings.memid = cd.members.memid 
GROUP BY firstname, surname 
ORDER BY rank, surname, firstname; 

Тем не менее, мне удалось ALM ost получить его сразу - из 30 записей, я получаю один краевой кейс, чье имя - «Ponder» и фамилия - «Stephens». Его округленное количество часов составляет 124.5, но решение настаивает на том, что округление его до ближайшего 10 должно привести к результату 120, в то время как мое решение производит 130.

(Кстати, есть несколько других примеры, такие как 204.5 округления до 210 как в шахте и решении при осуществлении автора.)

Что случилось с моим округлением логикой?

ответ

5

Если вы хотите, чтобы округлить до ближайшего 10, а затем использовать встроенный в round() функции:

select round(<whatever>, -1) 

Второй аргумент может быть отрицательным, с -1 в течение десятков, -2 сотни, и так на.

1

округление до ближайшего кратному 10:

round(<value>/10 - .5) * 10 

, которые завершат до значений, заканчивающихся в 5-9, вниз в противном случае.

Это имеет общую формулу:

round(<value>/<range> - .5) * <range> 

Таким образом, можно округлить до ближайшего 13, если вы хотите.

+0

Это округление, а не ближайшее несколько, например. 'SELECT round (105/50 + .5) * 50' будет давать' 150' вместо '100'. – Gajus

+0

@ Гаюс упс. Предполагалось, что это минус (не плюс). Исправлено, thx – Bohemian

0

Я боролся с эквивалентной проблемой. Мне нужно было округлить число до ближайшего кратного 50. Предложение Гордона здесь не работает.

Моя первая попытка была SELECT round(120/50) * 50, что дает 100. Однако SELECT round(130/50) * 50 дал 100. Это не верно; ближайший кратный - 150.

Хитрость заключается в разделении с использованием поплавка, например. SELECT round(130/50.0) * 50 собирается дать 150.

Оказывается, что делать x/y, где x и y целые числа, эквивалентно trunc(x/y). Где как плавающее деление правильно округляется до ближайшего кратного.