SQLで四捨五入するには ROUND(値, 桁数)、切り捨ては TRUNC(MySQLは TRUNCATE)か FLOOR、切り上げは CEIL(SQL Serverは CEILING)を使います。 ROUND の基本形は4つのデータベースで共通ですが、切り捨てと切り上げは関数名が少しずつ違い、負の数や FLOAT 型を渡したときの結果にも差が出ます。

この記事では、ROUND・TRUNC/TRUNCATE・CEIL/CEILING・FLOOR・MOD・ABS・POWER・SQRT・SIGN の9つを MySQL/PostgreSQL/Oracle/SQL Server の4列で並べた一覧表 にまとめました。さらに 2.5 や -2.5、桁数に負の値を渡した場合、FLOAT 型と DECIMAL 型で結果が変わる例 を、MySQL 8.0.46 と PostgreSQL 16.15 で実際に動かした出力つきで紹介します。消費税の端数処理や平均値の桁揃えなど、そのまま貼って使えるクエリも用意しました。

SQL数値関数の早見表(4つのDBで比較)

やりたいこと MySQL PostgreSQL Oracle SQL Server
四捨五入 ROUND(x, d) ROUND(x, d) ROUND(x, d) ROUND(x, d)(d は省略不可)
切り捨て(0 に近づける) TRUNCATE(x, d)(d は省略不可) TRUNC(x, d) TRUNC(x, d) ROUND(x, d, 1)(第3引数に 0 以外)
切り上げ(大きい方の整数へ) CEIL(x) / CEILING(x) CEIL(x) / CEILING(x) CEIL(x) CEILING(x)
小さい方の整数へ FLOOR(x) FLOOR(x) FLOOR(x) FLOOR(x)
余り MOD(a, b) / a % b / a MOD b MOD(a, b) / a % b MOD(a, b) a % b(MOD 関数はなし)
絶対値 ABS(x) ABS(x) ABS(x) ABS(x)
べき乗 POWER(x, y) / POW(x, y) POWER(x, y) / x ^ y POWER(x, y) POWER(x, y)
平方根 SQRT(x) SQRT(x) SQRT(x) SQRT(x)
符号(-1・0・1) SIGN(x) SIGN(x) SIGN(x) SIGN(x)
整数の割り算の商 a DIV b a / b(整数どうし)/ DIV(a, b) TRUNC(a / b) a / b(整数どうし)
表示用に桁を揃える FORMAT(x, d) TO_CHAR(x, 'FM999,990.0') TO_CHAR(x, 'FM999,990.0') FORMAT(x, 'N1')
  • d は「小数第何位まで残すか」 です。2 なら小数第2位まで、0 なら整数、-2 なら100の位までになります。
  • Oracle では従来 SELECT ROUND(2.5) FROM DUAL; のように FROM DUAL を付けて書きます。
  • MySQL と PostgreSQL の行は今回実行して確かめた内容です。Oracle と SQL Server の行は手元に実行環境がないため未実行 で、一般に知られた仕様として載せています(SQL Server の ROUND の第3引数だけは、Microsoft Learn の ROUND (Transact-SQL) のページを開いて確認しました)。

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「SQL ROUNDで四捨五入・切り捨て・切り上げ|数値関数一覧とDB別の違い」

四捨五入・切り捨て・切り上げの結果を値ごとに比べる

まず、同じ値を4種類の関数に渡したときの結果を1枚にまとめます。正の数では直感どおりですが、負の数で FLOOR と切り捨て(TRUNC)の結果が分かれる ところが要点です。

元の値 ROUND(四捨五入) TRUNC / TRUNCATE(切り捨て) CEIL(切り上げ) FLOOR
2.4 2 2 3 2
2.5 3 2 3 2
2.9 3 2 3 2
-2.4 -2 -2 -2 -3
-2.5 -3 -2 -2 -3
-0.5 -1 0 0 -1

上の表は DECIMAL(PostgreSQL では numeric)型の値を MySQL 8.0.46 と PostgreSQL 16.15 に渡した結果で、両者は一致しました。実際の出力は次のとおりです。

SELECT ROUND(2.5), ROUND(-2.5), ROUND(3.5), ROUND(2.4), ROUND(-2.4);
+------------+-------------+------------+------------+-------------+
| ROUND(2.5) | ROUND(-2.5) | ROUND(3.5) | ROUND(2.4) | ROUND(-2.4) |
+------------+-------------+------------+------------+-------------+
|          3 |          -3 |          4 |          2 |          -2 |
+------------+-------------+------------+------------+-------------+
SELECT CEIL(2.1), CEILING(2.1), CEIL(-2.5), FLOOR(2.9), FLOOR(-2.5);
+-----------+--------------+------------+------------+-------------+
| CEIL(2.1) | CEILING(2.1) | CEIL(-2.5) | FLOOR(2.9) | FLOOR(-2.5) |
+-----------+--------------+------------+------------+-------------+
|         3 |            3 |         -2 |          2 |          -3 |
+-----------+--------------+------------+------------+-------------+

覚え方は次の4つです。

  • ROUND:DECIMAL 型なら、ちょうど半分(.5)は 0 から遠い方へ。2.5 は 3、-2.5 は -3。
  • TRUNC / TRUNCATE:指定した桁より下を捨てる。常に 0 に近づく。
  • CEIL / CEILING:その値以上で最小の整数。数直線の右へ。
  • FLOOR:その値以下で最大の整数。数直線の左へ。

ROUNDで四捨五入する(桁数の指定と負の桁数)

ROUND(値, 桁数) の第2引数で、どの位まで残すかを決めます。

SELECT ROUND(1234.567, 2), ROUND(1234.567, 0), ROUND(1234.567, -2),
       ROUND(1250, -2), ROUND(-1250, -2);

MySQL 8.0.46 の実行結果です(PostgreSQL 16.15 でも同じ値でした)。

+--------------------+--------------------+---------------------+-----------------+------------------+
| ROUND(1234.567, 2) | ROUND(1234.567, 0) | ROUND(1234.567, -2) | ROUND(1250, -2) | ROUND(-1250, -2) |
+--------------------+--------------------+---------------------+-----------------+------------------+
|            1234.57 |               1235 |                1200 |            1300 |            -1300 |
+--------------------+--------------------+---------------------+-----------------+------------------+
桁数の指定 意味 1234.567 の結果
2 小数第3位を四捨五入し、小数第2位まで残す 1234.57
1 小数第2位を四捨五入し、小数第1位まで残す 1234.6
0(または省略) 小数第1位を四捨五入し、整数にする 1235
-1 1の位を四捨五入し、10の位まで残す 1230
-2 10の位を四捨五入し、100の位まで残す 1200
-3 100の位を四捨五入し、1000の位まで残す 1000

桁数に負の値を渡すと、小数点より左側(10の位、100の位…)で丸められます。 「100円単位にそろえる」「千人単位で表示する」といった場面で使えます。1250 を -2 で丸めると 1300、-1250 は -1300 になり、整数でも「半分は 0 から遠い方へ」の規則が当てはまりました。

DB ごとの注意点は次のとおりです。

DB ROUND の注意点
MySQL 桁数は省略可(省略時は 0)。文字列を渡すと数値に読み替えて計算する(ROUND('abc') は 0 になった)
PostgreSQL 桁数つきの ROUND(x, d) は numeric 型だけが対象。double precision を渡すとエラーになる(後述)
Oracle 桁数は省略可。NUMBER 型は .5 を 0 から遠い方へ丸める(未実行)
SQL Server 桁数は必須。第3引数に 0 以外を渡すと切り捨てになる。戻り値は入力と同じ型(未実行)

SQL Server では戻り値の型が入力と同じになるため、Microsoft Learn の例でも ROUND(150.75, 0) の結果は 151.00 と、小数部の桁が残った形で示されています。整数として表示したいときは CAST(ROUND(x, 0) AS INT) のように型変換を足します。

切り捨てる:TRUNC・TRUNCATE・FLOORとSQL ServerのROUND第3引数

切り捨ては DB によって関数名が違います。MySQL は TRUNCATE、PostgreSQL と Oracle は TRUNC、SQL Server は ROUND の第3引数 です。

MySQL:TRUNCATE(x, d)

SELECT TRUNCATE(1234.567, 2), TRUNCATE(1234.567, 0), TRUNCATE(1234.567, -2), TRUNCATE(-2.5, 0);
+-----------------------+-----------------------+------------------------+-------------------+
| TRUNCATE(1234.567, 2) | TRUNCATE(1234.567, 0) | TRUNCATE(1234.567, -2) | TRUNCATE(-2.5, 0) |
+-----------------------+-----------------------+------------------------+-------------------+
|               1234.56 |                  1234 |                   1200 |                -2 |
+-----------------------+-----------------------+------------------------+-------------------+

MySQL の TRUNCATE は 桁数を省略できません。引数を1つだけにすると構文エラーになります。

SELECT TRUNCATE(1234.567);
ERROR 1064 (42000) at line 4: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ')' at line 1

整数にしたいときは TRUNCATE(1234.567, 0) と 0 を明示します。

PostgreSQL:TRUNC(x, d)

SELECT TRUNC(1234.567, 2), TRUNC(1234.567), TRUNC(1234.567, -2), TRUNC(-2.5);
  trunc  | trunc | trunc | trunc 
---------+-------+-------+-------
 1234.56 |  1234 |  1200 |    -2

PostgreSQL に TRUNCATE という関数はありません(TRUNCATE はテーブルの全行を消す文です)。MySQL 向けのクエリをそのまま貼ると次のエラーになります。

ERROR:  function truncate(numeric, integer) does not exist
LINE 1: SELECT TRUNCATE(1234.567, 2);
               ^
HINT:  No function matches the given name and argument types. You might need to add explicit type casts.

Oracle と SQL Server(未実行)

-- Oracle:PostgreSQL と同じ TRUNC
SELECT TRUNC(1234.567, 2) FROM DUAL;   -- 1234.56

-- SQL Server:ROUND の第3引数に 0 以外を渡すと切り捨て
SELECT ROUND(1234.567, 2, 1);          -- 1234.560
SELECT ROUND(150.75, 0, 1);            -- 150.00(Microsoft Learn の例)

SQL Server の第3引数は「省略または 0 なら四捨五入、0 以外なら切り捨て」という意味です。1 と書くのが通例です。

FLOORとTRUNCの使い分け

正の数だけを扱うなら FLOOR(x) と TRUNC(x) は同じ結果になります。負の数が混ざるときに差が出ます。

値 FLOOR TRUNC / TRUNCATE 向いている用途
2.9 2 2 どちらでも同じ
-2.5 -3 -2 返品や差額など負の値がある集計では、どちらの意味にしたいかを先に決める
-0.9 -1 0 「端数を捨てる」なら TRUNC、「必ず小さい方へ」なら FLOOR

また、FLOOR は桁数を指定できません。小数第2位で切り捨てたいときは TRUNC(x, 2) か、次の形にします。

SELECT FLOOR(1234.567 * 100) / 100 AS floor_2;   -- 1234.56

切り上げる:CEIL・CEILINGと小数第n位での切り上げ

切り上げの標準的な関数は CEIL / CEILING です。MySQL と PostgreSQL は両方の名前が使え、Oracle は CEIL、SQL Server は CEILING と覚えると迷いません。切り上げ用の関数を自作しなくても、まずはこの関数で足ります。

SELECT CEIL(2.0), CEIL(2.0000001), FLOOR(-0.5), ROUND(-0.4);
+-----------+-----------------+-------------+-------------+
| CEIL(2.0) | CEIL(2.0000001) | FLOOR(-0.5) | ROUND(-0.4) |
+-----------+-----------------+-------------+-------------+
|         2 |               3 |          -1 |           0 |
+-----------+-----------------+-------------+-------------+

ぴったり整数ならそのまま、わずかでも超えていれば次の整数になります。

小数第2位までで切り上げる

CEIL は整数にしかできないので、いったん 100 倍して切り上げ、100 で割り戻します。

SELECT CEIL(1234.561 * 100) / 100 AS ceil_2;
+-----------+
| ceil_2    |
+-----------+
| 1234.5700 |
+-----------+

MySQL では 1234.5700、PostgreSQL では 1234.5700000000000000 と、割り算の結果は小数部の桁が長く表示されました。表示を整えるなら ROUND(CEIL(x * 100) / 100, 2) と外側に ROUND を足します。

100の位で切り上げる(100円単位など)

-- MySQL
SELECT CEIL(price / 100) * 100 FROM items;
-- PostgreSQL(整数どうしの割り算は整数になるため 100.0 で割る)
SELECT CEIL(price / 100.0) * 100 FROM items;

負の数を「0 から遠い方へ」切り上げる

CEIL(-2.5) は -2 です。絶対値が大きくなる方向(-3)へ切り上げたいときは、ABS と SIGN を組み合わせます。

SELECT SIGN(-12.345) * CEIL(ABS(-12.345) * 10) / 10  AS up_away,   -- 0から遠い方へ
       SIGN(-12.345) * FLOOR(ABS(-12.345) * 10) / 10 AS down_zero; -- 0に近い方へ
+----------+-----------+
| up_away  | down_zero |
+----------+-----------+
| -12.4000 |  -12.3000 |
+----------+-----------+

符号を SIGN で取り出し、絶対値に対して切り上げてから符号を戻す、という流れです。Oracle でも CEIL・ABS・SIGN はそのまま使えるので、同じ式で書けます(未実行)。

FLOAT型とDECIMAL型で結果が変わる例

ここが一番の落とし穴です。同じ 2.5 でも、DECIMAL(正確な10進数)として渡すか FLOAT/DOUBLE(2進数の近似値)として渡すかで、ROUND の結果が変わります。

2.5 を丸めると 3 にも 2 にもなる

MySQL では 2.5 と書くと DECIMAL、2.5E0 と書くと DOUBLE として扱われます。

SELECT ROUND(2.5) AS dec_val, ROUND(2.5E0) AS float_val,
       ROUND(3.5E0) AS f35, ROUND(-2.5E0) AS fneg;
+---------+-----------+-----+------+
| dec_val | float_val | f35 | fneg |
+---------+-----------+-----+------+
|       3 |         2 |   4 |   -2 |
+---------+-----------+-----+------+

PostgreSQL 16.15 でも同じ傾向でした。

SELECT ROUND(2.5) AS dec_val, ROUND(2.5::float8) AS float_val,
       ROUND(3.5::float8) AS f35, ROUND(-2.5::float8) AS fneg,
       ROUND(0.5::float8) AS f05, ROUND(1.5::float8) AS f15;
 dec_val | float_val | f35 | fneg | f05 | f15 
---------+-----------+-----+------+-----+-----
       3 |         2 |   4 |   -2 |   0 |   2

浮動小数点型では、ちょうど半分の値が「近い方の偶数」に丸められました(2.5 は 2、3.5 は 4、0.5 は 0、1.5 は 2)。これは「偶数丸め」と呼ばれる方式で、たくさんの値を丸めたときに合計が一方向へずれにくい性質があります。学校で習う四捨五入と同じ結果がほしいときは、DECIMAL/NUMERIC 型で計算します。

テーブルの列でも同じことが起きる

CREATE TABLE num_test (id INT PRIMARY KEY, f FLOAT, d DOUBLE, n DECIMAL(10,3));
INSERT INTO num_test VALUES
  (1, 2.5, 2.5, 2.5), (2, 1.005, 1.005, 1.005),
  (3, 0.285, 0.285, 0.285), (4, 2.675, 2.675, 2.675);

SELECT id, ROUND(f, 2) AS float_r, ROUND(d, 2) AS double_r, ROUND(n, 2) AS decimal_r
FROM num_test;

MySQL 8.0.46 の実行結果です。

+----+---------+----------+-----------+
| id | float_r | double_r | decimal_r |
+----+---------+----------+-----------+
|  1 |     2.5 |      2.5 |      2.50 |
|  2 |       1 |        1 |      1.01 |
|  3 |    0.28 |     0.28 |      0.29 |
|  4 |    2.67 |     2.68 |      2.68 |
+----+---------+----------+-----------+
入れた値 FLOAT 列 DOUBLE 列 DECIMAL 列 理由
1.005 1 1 1.01 2進数では 1.005 をぴったり表せず、わずかに小さい値として保存される
0.285 0.28 0.28 0.29 同上
2.675 2.67 2.68 2.68 FLOAT(単精度)と DOUBLE(倍精度)で近似値が違う

DECIMAL 列だけが、紙の上の計算と同じ結果になりました。 金額・数量・比率など、端数処理の結果を説明できる必要がある列は DECIMAL/NUMERIC で定義しておくと安心です。

近似値であることは、足し算でも確かめられます。

SELECT 0.1E0 + 0.2E0 AS float_sum, 0.1 + 0.2 AS dec_sum,
       0.1E0 + 0.2E0 = 0.3E0 AS float_eq, 0.1 + 0.2 = 0.3 AS dec_eq;
+---------------------+---------+----------+--------+
| float_sum           | dec_sum | float_eq | dec_eq |
+---------------------+---------+----------+--------+
| 0.30000000000000004 |     0.3 |        0 |      1 |
+---------------------+---------+----------+--------+

PostgreSQLでは桁数つきROUNDにfloat8を渡せない

PostgreSQL の ROUND(x, d) は numeric 型専用です。double precision の列にそのまま使うと次のエラーになります。

SELECT ROUND(1.005::float8, 2);
ERROR:  function round(double precision, integer) does not exist
LINE 1: SELECT ROUND(1.005::float8, 2);
               ^
HINT:  No function matches the given name and argument types. You might need to add explicit type casts.

直し方は、numeric に型変換してから丸めることです。

SELECT ROUND(1.005::float8::numeric, 2) AS via_numeric,
       ROUND(CAST(2.675::float8 AS numeric), 2) AS v2;
 via_numeric |  v2  
-------------+------
        1.01 | 2.68

PostgreSQL 16.15 では、real 列・double precision 列を ::numeric に変換してから丸めると、4行とも DECIMAL と同じ結果(2.50/1.01/0.29/2.68)になりました。同じデータでも MySQL の FLOAT 列とは結果が違うので、DB を移行するときは丸め結果を数件見比べる ことをおすすめします。

切り捨て・切り上げでも1円ずれる

SELECT FLOOR(100 * 1.1E0) AS f100, CEIL(100 * 1.1E0) AS c100, CEIL(100 * 1.1) AS cd100;
+------+------+-------+
| f100 | c100 | cd100 |
+------+------+-------+
|  110 |  111 |   110 |
+------+------+-------+

100 × 1.1 は 110 ちょうどのはずですが、浮動小数点で計算すると 110 をほんの少し超えた値になり、CEIL で 111 に切り上がりました(MySQL・PostgreSQL とも同じ結果)。税率や係数は 1.1E0 や FLOAT 列ではなく、1.1 のような DECIMAL リテラルか DECIMAL 列で持つと、こうしたずれを避けられます。

DB 2.5 と書いたときの型 浮動小数点として渡す書き方
MySQL DECIMAL 2.5E0、FLOAT/DOUBLE 列
PostgreSQL numeric 2.5::float8、real/double precision 列
Oracle NUMBER BINARY_FLOAT/BINARY_DOUBLE 列(未実行)
SQL Server decimal(numeric) 2.5E0、float/real 列(未実行)

MOD・ABS・SIGN・POWER・SQRTの使い方

丸め以外でよく使う数値関数をまとめて確認します。

MOD:余りを求める

SELECT MOD(7, 3), 7 % 3, MOD(-7, 3), MOD(7, -3), MOD(7.5, 2);
+-----------+-------+------------+------------+-------------+
| MOD(7, 3) | 7 % 3 | MOD(-7, 3) | MOD(7, -3) | MOD(7.5, 2) |
+-----------+-------+------------+------------+-------------+
|         1 |     1 |         -1 |          1 |         1.5 |
+-----------+-------+------------+------------+-------------+

余りの符号は「割られる数」と同じになります(-7 を 3 で割った余りは -1)。MySQL と PostgreSQL で同じ結果でした。「偶数か奇数か」を判定するときは MOD(x, 2) = 0 と 0 との比較にしておくと、負の数でも正しく判定できます(= 1 と書くと、負の奇数は -1 なので当てはまりません)。

0 で割ったときの挙動は DB で分かれます。

式 MySQL 8.0.46 PostgreSQL 16.15
MOD(7, 0) NULL ERROR: division by zero
7 / 0 NULL ERROR: division by zero
SQRT(-1) NULL ERROR: cannot take square root of a negative number

0 で割る可能性があるときは NULLIF で 0 を NULL に変えておくと、どの DB でもエラーにならず NULL が返ります。

SELECT total / NULLIF(cnt, 0) FROM summary;

SQL Server には MOD 関数がないので 7 % 3 と書きます。Oracle は MOD(7, 3) です(いずれも未実行)。

ABS・SIGN:絶対値と符号

SELECT ABS(-5), ABS(5), ABS(-2.5), SIGN(-8), SIGN(0), SIGN(3.2);
+---------+--------+-----------+----------+---------+-----------+
| ABS(-5) | ABS(5) | ABS(-2.5) | SIGN(-8) | SIGN(0) | SIGN(3.2) |
+---------+--------+-----------+----------+---------+-----------+
|       5 |      5 |       2.5 |       -1 |       0 |         1 |
+---------+--------+-----------+----------+---------+-----------+
関数 使いどころ
ABS(a - b) 2つの値の差の大きさ(どちらが大きくても正の数で得られる)
ORDER BY ABS(x - 100) 100 に近い順に並べる
SIGN(x) 増減を -1・0・1 の3値にまとめる。CASE と組み合わせて「増加/変化なし/減少」のラベルにする

POWER・SQRT:べき乗と平方根

SELECT POWER(2, 10), POWER(10, -2), SQRT(16), SQRT(2);
+--------------+---------------+----------+--------------------+
| POWER(2, 10) | POWER(10, -2) | SQRT(16) | SQRT(2)            |
+--------------+---------------+----------+--------------------+
|         1024 |          0.01 |        4 | 1.4142135623730951 |
+--------------+---------------+----------+--------------------+

MySQL は POW という別名も使えます。PostgreSQL は 2 ^ 10 という演算子でも書け、1024 が返りました。PostgreSQL の POWER(2, 10) と SQRT(16) の戻り値は double precision 型 でした(pg_typeof で確認)。この結果をさらに ROUND(…, 2) で丸めるときは、前述のとおり ::numeric への型変換が必要です。

SQL Server の POWER は、戻り値が第1引数と同じ型になることが知られています。POWER(2, 0.5) のように第1引数が整数だと結果も整数になるため、小数の結果がほしいときは POWER(2.0, 0.5) や POWER(CAST(2 AS FLOAT), 0.5) と書きます(未実行)。

整数どうしの割り算はDBで結果が違う

丸め関数とセットでつまずきやすいのが割り算です。7 / 2 の結果は DB によって 3.5 にも 3 にもなります。

式 MySQL 8.0.46 PostgreSQL 16.15
7 / 2 3.5000 3(整数どうしは整数)
-7 / 2 -3.5000 -3(0 に近づく)
7 / 2.0 3.5000 3.5000000000000000
商だけ取り出す 7 DIV 2 は 3、-7 DIV 2 は -3 DIV(7, 2) は 3
FLOOR(-7 / 2.0) -4 -4

SQL Server も整数どうしは整数になり、Oracle は 3.5 になることが知られています(未実行)。どの DB でも同じ結果にしたいときは、片方を 2.0 や CAST(x AS DECIMAL(10,2)) で小数にしてから割る と安全です。

コピペ用クエリ集(消費税・平均・構成比・単位そろえ)

ここからは、そのまま貼って試せるクエリです。次のテーブルを使います(MySQL・PostgreSQL 共通)。

CREATE TABLE items (
  item_id   INT PRIMARY KEY,
  item_name VARCHAR(30),
  price     INT,
  category  VARCHAR(10)
);
INSERT INTO items VALUES
  (1,'ノート',198,'文具'),(2,'ボールペン',105,'文具'),
  (3,'マグカップ',1280,'雑貨'),(4,'卓上ライト',3333,'雑貨'),(5,'付箋',88,'文具');

消費税の計算(切り捨て・四捨五入・切り上げ)

SELECT item_name, price,
       price * 1.1          AS raw_val,
       FLOOR(price * 1.1)   AS tax_floor,   -- 税込(端数切り捨て)
       ROUND(price * 1.1)   AS tax_round,   -- 税込(端数四捨五入)
       CEIL(price * 1.1)    AS tax_ceil,    -- 税込(端数切り上げ)
       FLOOR(price * 0.1)   AS tax_only     -- 税額のみ(切り捨て)
FROM items;

PostgreSQL 16.15 の実行結果です(MySQL 8.0.46 でも同じ値でした)。

 item_name  | price | raw_val | tax_floor | tax_round | tax_ceil | tax_only 
------------+-------+---------+-----------+-----------+----------+----------
 ノート     |   198 |   217.8 |       217 |       218 |      218 |       19
 ボールペン |   105 |   115.5 |       115 |       116 |      116 |       10
 マグカップ |  1280 |  1408.0 |      1408 |      1408 |     1408 |      128
 卓上ライト |  3333 |  3666.3 |      3666 |      3666 |     3667 |      333
 付箋       |    88 |    96.8 |        96 |        97 |       97 |        8

端数処理を切り捨て・四捨五入・切り上げのどれにするかは、自社の決まりに合わせて選びます。税率は 1.1 と DECIMAL リテラルで書く のがポイントです。SQL Server では CEIL を CEILING に置き換えます。

1件ごとに丸めてから合計する場合と、合計してから丸める場合では結果が変わることがあるので、どちらで計算するかも決めておきましょう。

SELECT SUM(FLOOR(price * 1.1)) AS sum_of_rounded,   -- 1件ごとに切り捨ててから合計
       FLOOR(SUM(price) * 1.1) AS rounded_sum       -- 合計してから切り捨て
FROM items;

MySQL 8.0.46 で実行すると、前者は 5502(217 + 115 + 1408 + 3666 + 96)、後者は 5504(5004 × 1.1 = 5504.4 を切り捨て)と、2 の差が出ました。

平均値の表示桁を揃える

-- MySQL
SELECT category,
       AVG(price)                          AS avg_raw,
       ROUND(AVG(price), 1)                AS avg_1,
       FORMAT(AVG(price), 1)               AS avg_fmt,
       CAST(AVG(price) AS DECIMAL(10,1))   AS avg_cast
FROM items GROUP BY category;
+----------+-----------+--------+---------+----------+
| category | avg_raw   | avg_1  | avg_fmt | avg_cast |
+----------+-----------+--------+---------+----------+
| 文具     |  130.3333 |  130.3 | 130.3   |    130.3 |
| 雑貨     | 2306.5000 | 2306.5 | 2,306.5 |   2306.5 |
+----------+-----------+--------+---------+----------+
-- PostgreSQL
SELECT category,
       AVG(price)                                  AS avg_raw,
       ROUND(AVG(price), 1)                        AS avg_1,
       TO_CHAR(AVG(price), 'FM999,999,990.0')      AS avg_fmt,
       CAST(AVG(price) AS NUMERIC(10,1))           AS avg_cast
FROM items GROUP BY category ORDER BY category;
 category |        avg_raw        | avg_1  | avg_fmt | avg_cast 
----------+-----------------------+--------+---------+----------
 文具     |  130.3333333333333333 |  130.3 | 130.3   |    130.3
 雑貨     | 2306.5000000000000000 | 2306.5 | 2,306.5 |   2306.5
方法 戻り値 向いている場面
ROUND(x, 1) 数値 丸めた値でさらに計算や並べ替えをする
CAST(x AS DECIMAL(10,1)) 数値(四捨五入される) 桁数を型として固定したい
FORMAT / TO_CHAR 文字列 3桁区切りのカンマなど、画面や帳票に出す直前の整形

FORMAT や TO_CHAR の結果は文字列なので、ORDER BY に使うと文字の順で並びます。 並べ替えは元の数値列で行い、整形は SELECT の一番外側だけにすると混乱しません。

MySQL では、整数に ROUND(5, 1) を使っても 5 のままで、末尾に .0 は付きませんでした(PostgreSQL は 5.0)。「5.0」と必ず小数第1位まで表示したいときは CAST(5 AS DECIMAL(10,1)) か FORMAT(5, 1) を使います。

構成比(パーセント)を小数第1位まで出す

SELECT category, COUNT(*) AS cnt,
       ROUND(COUNT(*) * 100 / SUM(COUNT(*)) OVER (), 1) AS pct
FROM items GROUP BY category;
+----------+-----+------+
| category | cnt | pct  |
+----------+-----+------+
| 文具     |   3 | 60.0 |
| 雑貨     |   2 | 40.0 |
+----------+-----+------+

COUNT(*) * 100 / 件数 のように整数だけで割ると、PostgreSQL や SQL Server では小数部が消えます。100.0 を掛けるか、上のように numeric を返す SUM(...) OVER () で割ると小数が残ります。

100の位・10の位で単位をそろえる

-- MySQL(PostgreSQL は TRUNCATE を TRUNC に、price / 100 を price / 100.0 に変える)
SELECT item_name, price,
       TRUNCATE(price, -2)       AS hundred_floor,  -- 100未満を切り捨て
       CEIL(price / 100) * 100   AS hundred_ceil,   -- 100単位に切り上げ
       ROUND(price, -1)          AS ten_round       -- 1の位を四捨五入
FROM items;
+-----------------+-------+---------------+--------------+-----------+
| item_name       | price | hundred_floor | hundred_ceil | ten_round |
+-----------------+-------+---------------+--------------+-----------+
| ノート          |   198 |           100 |          200 |       200 |
| ボールペン      |   105 |           100 |          200 |       110 |
| マグカップ      |  1280 |          1200 |         1300 |      1280 |
| 卓上ライト      |  3333 |          3300 |         3400 |      3330 |
| 付箋            |    88 |             0 |          100 |        90 |
+-----------------+-------+---------------+--------------+-----------+

MODで行をグループに分ける

SELECT item_id, MOD(item_id, 2) AS odd_flag, MOD(item_id, 3) AS grp FROM items;
+---------+----------+------+
| item_id | odd_flag | grp  |
+---------+----------+------+
|       1 |        1 |    1 |
|       2 |        0 |    2 |
|       3 |        1 |    0 |
|       4 |        0 |    1 |
|       5 |        1 |    2 |
+---------+----------+------+

MOD(id, N) は、行を N 個のグループにほぼ均等に分けたいとき(分割して処理する、交互に色を付けるなど)に使えます。SQL Server では item_id % 3 と書きます。

つまずきやすいエラーと直し方

出たメッセージ・現象 DB 原因 直し方
function round(double precision, integer) does not exist PostgreSQL 桁数つき ROUND に float8 を渡した ROUND(x::numeric, 2)
function truncate(numeric, integer) does not exist PostgreSQL MySQL の関数名のまま TRUNC(x, 2) に変える
ERROR 1064 ... near ')' MySQL TRUNCATE(x) と桁数を省略した TRUNCATE(x, 0)
division by zero PostgreSQL 0 で割った x / NULLIF(y, 0)
cannot take square root of a negative number PostgreSQL 負の数の平方根 SQRT(ABS(x)) や CASE で条件を分ける
0 で割っても NULL が返るだけで気づきにくい MySQL SELECT では NULL になる仕様 NULL を COALESCE で扱うか、事前に条件で除外する
2.5 が 2 に丸められる MySQL・PostgreSQL 浮動小数点型は偶数丸めになった DECIMAL/NUMERIC に変換してから丸める
100 × 1.1 の切り上げが 111 になる MySQL・PostgreSQL 係数が浮動小数点 係数を 1.1(DECIMAL)で持つ
割合がすべて 0 になる PostgreSQL・SQL Server 整数どうしの割り算 100.0 を掛ける、または CAST する
丸めたのに 5 と表示され 5.0 にならない MySQL 整数を丸めても整数のまま CAST(x AS DECIMAL(10,1))

NULL を渡したときは、MySQL・PostgreSQL とも ROUND(NULL) や ABS(NULL) は NULL を返しました。NULL を 0 として扱いたいときは ROUND(COALESCE(x, 0), 1) のように先に置き換えます。

どの関数を使うかの判断表

やりたいこと 選ぶ関数 補足
ふつうの四捨五入 ROUND(x, d) 値は DECIMAL/NUMERIC 型で
端数を捨てる(負の数も 0 に近づける) TRUNC / TRUNCATE / SQL Server は ROUND(x, d, 1) 桁数を指定できる
端数を捨てる(正の数のみ、整数に) FLOOR(x) 4つの DB で同じ名前
端数があれば繰り上げる CEIL / CEILING 必要な箱数・ページ数の計算に
小数第 n 位で切り上げ・切り捨て CEIL(x * 10^n) / 10^n FLOOR も同じ形
10・100・1000 単位にそろえる ROUND(x, -1)、ROUND(x, -2) など 桁数に負の値
表示だけ桁を揃える FORMAT / TO_CHAR 結果は文字列
偶数・奇数、N 個に分ける MOD(x, N)(SQL Server は %) 判定は = 0 で
差の大きさ ABS(a - b)
増減の向き SIGN(a - b) -1・0・1

「必要なページ数」は CEIL の定番の使い道です。たとえば 53 件を 1 ページ 10 件で表示するなら CEIL(53 / 10.0) で 6 ページと求められます(MySQL 8.0.46 で 6 が返ることを確認)。

よくある質問

Q. ROUND はどのデータベースでも同じ結果になりますか? A. DECIMAL/NUMERIC 型の値に対する ROUND(x, d) は、今回試した MySQL 8.0.46 と PostgreSQL 16.15 で同じ結果でした。差が出るのは、浮動小数点型を渡したとき(2.5 が 2 になる)、PostgreSQL で桁数つき ROUND に float8 を渡したとき(エラー)、SQL Server で桁数を省略したとき(必須)などです。

Q. 切り上げの関数は CEIL と CEILING のどちらを書けばよいですか? A. MySQL と PostgreSQL はどちらも使えます。Oracle は CEIL、SQL Server は CEILING です。複数の DB で動かす SQL を書くなら、対象の DB に合わせて置き換える前提にしておくとよいでしょう。

Q. FLOOR と TRUNC(TRUNCATE)は何が違いますか? A. 正の数では同じ結果です。負の数では、FLOOR は小さい方(-2.5 なら -3)、TRUNC は 0 に近い方(-2.5 なら -2)になります。また TRUNC は小数第何位まで残すかを指定できます。

Q. 金額の列は FLOAT と DECIMAL のどちらにすべきですか? A. 端数処理の結果を説明できる必要がある値は DECIMAL/NUMERIC が向いています。今回の検証でも、FLOAT 列の 1.005 は 1 に、DECIMAL 列の 1.005 は 1.01 に丸められました。FLOAT/DOUBLE は、計測値のように近似で十分な値に向いています。

まとめ

  • 四捨五入は ROUND(x, d)。d が正なら小数側、負なら 10 の位・100 の位で丸める。
  • 切り捨ては MySQL が TRUNCATE(x, d)、PostgreSQL と Oracle が TRUNC(x, d)、SQL Server が ROUND(x, d, 1)。
  • 切り上げは CEIL / CEILING。小数第 n 位で切り上げたいときは「掛けて、切り上げて、割り戻す」。
  • 負の数では FLOOR(-2.5 は -3)と TRUNC(-2.5 は -2)の結果が分かれる。
  • FLOAT/DOUBLE 型では 2.5 が 2 に丸められた。四捨五入の結果を揃えたい値は DECIMAL/NUMERIC で扱う。
  • PostgreSQL の桁数つき ROUND は numeric 専用。float8 は ::numeric に変換してから丸める。
  • 整数どうしの割り算や 0 での割り算は DB で結果が違うので、2.0 で割る・NULLIF を使うといった書き方をそろえておく。

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「SQL ROUNDで四捨五入・切り捨て・切り上げ|数値関数一覧とDB別の違い」

株式会社RJCでは、SQLの数値関数や端数処理の考え方を仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?