web-dev-qa-db-ja.com

Postgresで数値を最も近い10に丸める

私はPGExercises.comからこの特定の問題を解決しようとしています:

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

質問の要点は、クラブメンバーの表と、彼らが予約した30分の時間枠が与えられていることです(リストを取得するのは簡単です) 2つのテーブルの内部結合)。

私は、予約された合計時間でメンバーの降順のランキングを作成し、最も近い10。また、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初心者として、私の非常に不誠実な解決策ははるかに複雑で、CASE WHENを使用し、最後の桁を見て丸めるかどうかを決定するために数値をテキストに変換します upまたはdown

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;

それでも、私はほぼ正しくそれを得ることができます-30のレコードのうち、firstnameが 'Ponder'およびlastnameは「Stephens」です。彼の丸められた時間数は124.5ですが、ソリューションでは、10に最も近い値に丸めると、120の結果が生成され、私のソリューションでは130が生成されると主張しています。

(ちなみに、私のものと演習の作成者のソリューションの両方で、204.5210に切り上げるなど、他にもいくつかの例があります。)

丸めロジックの何が問題になっていますか?

7
neuron

10に最も近い値に丸める場合は、組み込みのround()関数を使用します。

select round(<whatever>, -1)

2番目の引数は負の値にすることができ、-1は数十、-2は数百というようになります。

24
Gordon Linoff

最も近い10の倍数に丸めるには:

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

これは、5〜9で終わる値の場合はpを丸め、それ以外の場合は切り捨てます。

これには、次の一般式があります。

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

したがって、必要に応じて、最も近い13に丸めることができます。

2
Bohemian

私は同等の問題に苦しんでいます。数値を50の最も近い倍数に丸める必要がありました。ここでのゴードンの提案は機能しません。

私の最初の試みはSELECT round(120 / 50) * 50で、これは_100_を与えます。ただし、SELECT round(130 / 50) * 50は_100_を与えました。これは間違っています;最も近い倍数は_150_です。

秘訣は、フロートを使用して分割することです。 SELECT round(130 / 50.0) * 50は_150_を提供します。

xyが整数である_x/y_を実行することは、trunc(x/y)と同等であることがわかります。一方、浮動小数点除算は最も近い倍数に正しく丸められます。

0
Gajus