MySQL 半场
MySQL round half
是否可以在 MySQL 中像 PHP 那样以特定方式四舍五入?
PHP_ROUND_HALF_UP
PHP_ROUND_HALF_DOWN
PHP_ROUND_HALF_EVEN
PHP_ROUND_HALF_ODD
或者我们真的只限于四舍五入吗?
1。来自 Oracle 文档:
根据MySQL官方文档,舍入函数如下:
For exact-value numbers, ROUND() uses the “round half away from zero”
or “round toward nearest” rule: A value with a fractional part of .5
or greater is rounded up to the next integer if positive or down to
the next integer if negative. (In other words, it is rounded away from
zero.) A value with a fractional part less than .5 is rounded down to
the next integer if positive or up to the next integer if negative.
http://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html#function_round
也就是说我们只能使用四舍五入函数来向上取整。我编写了以下 UDF 来解决此限制。
2。代码(在MySQL 5.6中测试):
CREATE FUNCTION roundHalf (
numberToRound DECIMAL(20,6),
roundingPrecision TINYINT(2),
roundingType ENUM (
'ROUND_HALF_UP',
'ROUND_HALF_DOWN',
'ROUND_HALF_EVEN',
'ROUND_HALF_ODD'
)
)
RETURNS DECIMAL(20,6)
BEGIN
DECLARE digitEvenOdd TINYINT (2) UNSIGNED DEFAULT 255;
DECLARE digitPosition TINYINT (2) UNSIGNED DEFAULT 0;
DECLARE digitToRound TINYINT (2) DEFAULT -1;
DECLARE roundedNumber DECIMAL(20,6) DEFAULT 0;
SET digitPosition = INSTR(numberToRound, '.');
IF (roundingPrecision < 1) THEN
SET digitPosition = digitPosition + roundingPrecision;
ELSE
SET digitPosition = digitPosition + roundingPrecision + 1;
END IF;
IF (digitPosition > 0 AND
digitPosition <= CHAR_LENGTH(numberToRound)
) THEN
SET digitToRound = CAST(
SUBSTR(
numberToRound,
digitPosition,
1
) AS UNSIGNED
);
SET digitPosition = digitPosition - 1;
IF (digitPosition > 0 AND
digitPosition <= CHAR_LENGTH(numberToRound)
) THEN
SET digitEvenOdd = CAST(
SUBSTR(
numberToRound,
digitPosition,
1
) AS UNSIGNED
);
END IF;
END IF;
IF (digitToRound > -1) THEN
CASE roundingType
WHEN 'ROUND_HALF_UP' THEN
IF (digitToRound >= 5) THEN
SET roundedNumber = ROUND(numberToRound, roundingPrecision);
ELSE
SET roundedNumber = TRUNCATE(numberToRound, roundingPrecision);
END IF;
WHEN 'ROUND_HALF_DOWN' THEN
IF (digitToRound > 5) THEN
SET roundedNumber = ROUND(numberToRound, roundingPrecision);
ELSE
SET roundedNumber = TRUNCATE(numberToRound, roundingPrecision);
END IF;
WHEN 'ROUND_HALF_EVEN' THEN
IF (digitToRound >= 5 AND
digitEvenOdd IN (1,3,5,7,9)
) THEN
SET roundedNumber = ROUND(numberToRound, roundingPrecision);
ELSE
SET roundedNumber = TRUNCATE(numberToRound, roundingPrecision);
END IF;
WHEN 'ROUND_HALF_ODD' THEN
IF (digitToRound >= 5 AND
digitEvenOdd IN (0,2,4,6,8)
) THEN
SET roundedNumber = ROUND(numberToRound, roundingPrecision);
ELSE
SET roundedNumber = TRUNCATE(numberToRound, roundingPrecision);
END IF;
END CASE;
ELSEIF (roundingPrecision > 0) THEN
SET roundedNumber = numberToRound;
END IF;
RETURN roundedNumber;
END //
注意:四舍五入的数值可以根据项目的需要增加到总共65位(在这种情况下,不要忘记更改所有DECIMAL(20,6)
相应的实例)。
3。舍入结果为 PHP:
http://sandbox.onlinephpfunctions.com/code/054de06b074c2b3ece5fb6e5d4180524cd2207e2
4。单元测试(在 SQL 中):
/* round number not enough digits */
IF (roundHalf(1.455, 7, 'ROUND_HALF_UP') <> 1.455) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_ODD_TEST_1';
ELSEIF (roundHalf(1.455, -5, 'ROUND_HALF_UP') <> 0) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_ODD_TEST_2';
ELSEIF (roundHalf(555, -1, 'ROUND_HALF_UP') <> 560) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_ODD_TEST_3';
END IF;
/* round half up */
IF (roundHalf(1.541, 2, 'ROUND_HALF_UP') <> 1.54) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_UP_TEST_1';
ELSEIF (roundHalf(1.545, 2, 'ROUND_HALF_UP') <> 1.55) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_UP_TEST_2';
ELSEIF (roundHalf(555, 0, 'ROUND_HALF_UP') <> 555) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_UP_TEST_3';
ELSEIF (roundHalf(1000999, -2, 'ROUND_HALF_UP') <> 1001000) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_UP_TEST_4';
ELSEIF (roundHalf(1000999, -3, 'ROUND_HALF_UP') <> 1001000) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_UP_TEST_5';
ELSEIF (roundHalf(1000999, -4, 'ROUND_HALF_UP') <> 1000000) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_UP_TEST_6';
END IF;
/* round half down */
IF (roundHalf(1.541, 2, 'ROUND_HALF_DOWN') <> 1.54) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_DOWN_TEST_1';
ELSEIF (roundHalf(1.545, 2, 'ROUND_HALF_DOWN') <> 1.54) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_DOWN_TEST_2';
ELSEIF (roundHalf(555, 0, 'ROUND_HALF_DOWN') <> 555) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_DOWN_TEST_3';
ELSEIF (roundHalf(1000999, -2, 'ROUND_HALF_DOWN') <> 1001000) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_UP_TEST_4';
ELSEIF (roundHalf(1000999, -3, 'ROUND_HALF_DOWN') <> 1001000) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_DOWN_TEST_5';
ELSEIF (roundHalf(1000999, -4, 'ROUND_HALF_DOWN') <> 1000000) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_DOWN_TEST_6';
END IF;
/* round half even */
IF (roundHalf(1.541, 2, 'ROUND_HALF_EVEN') <> 1.54) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_EVEN_TEST_1';
ELSEIF (roundHalf(1.544, 2, 'ROUND_HALF_EVEN') <> 1.54) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_EVEN_TEST_2';
ELSEIF (roundHalf(1.455, 2, 'ROUND_HALF_EVEN') <> 1.46) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_EVEN_TEST_3';
END IF;
/* round half odd */
IF (roundHalf(1.544, 2, 'ROUND_HALF_ODD') <> 1.54) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_ODD_TEST_1';
ELSEIF (roundHalf(1.545, 2, 'ROUND_HALF_ODD') <> 1.55) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_ODD_TEST_2';
ELSEIF (roundHalf(1.455, 2, 'ROUND_HALF_ODD') <> 1.45) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_ODD_TEST_3';
END IF;
随意使用该编码,但不要忘记喜欢我的post。谢谢大家的意见和建议。
可以使用 ROUND
函数在一行中完成四舍五入:
SELECT ROUND(ROUND({your_input_value}*2,0)/2,1);
- 第一个
ROUND({your_input_value}*2,0)
为您提供一个四舍五入的整数值。
- 第二个 ROUND(.../2,1) 为您提供一位数结果,而不是更长一点的结果,其中包含更多零和可能的浮点偏差。
可以创建具有向上舍入、向下舍入或其他乘数(如 3,4 等)的类似其他回合。
MySQL 中的 ROUND()
函数在相当长一段时间内存在一个错误,它 returns 不一致的结果取决于它是否认为输入值是四舍五入 精确或近似数字。至少记录了几个错误(参见 here as well as MySQL 5.7 description of the behavour here)。
我遇到了 MySQL 5.6.22 的问题,但从 2001 年的一些讨论来看,它似乎相当普遍。它似乎已在 2017 年底和 2018 年初得到修复,但我不能证明哪些版本可以正常工作,哪些不能。
以下是一种解决方法,无论 <input_value>
是否被 MySQL 视为 exact[=35=,都可以使结果一致地保留一位小数] 或 近似值 数:
SELECT ROUND(CAST(<input_value> AS DECIMAL(10,2)), 1)
如果需要四舍五入到更多的地方,将第二个参数ROUND()
从1
改为你需要的小数位数,将第二个参数从DECIMAL()
改为DECIMAL()
2
设置为比您需要的小数位数高一位的值。例如。对于小数点后 3 位,您可以使用:
SELECT ROUND(CAST(<input_value> AS DECIMAL(10,4)), 3)
请注意,DECIMAL()
的第一个参数是数字中的总位数,因此,如果您需要存储较大的值,则可能需要相应地进行调整。有关详细信息,请参阅 MySQL manual。
如果您在应用@Jonathan 的函数时遇到错误,请说:
Error 1418: This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration and binary logging is enabled (you *might* want to use the less safe log_bin_trust_function_creators variable) SQL Statement:...
您应该在 BEGIN
之前放置 DETERMINISTIC
或其他适用的声明
DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration and binary logging is enabled
是否可以在 MySQL 中像 PHP 那样以特定方式四舍五入?
PHP_ROUND_HALF_UP
PHP_ROUND_HALF_DOWN
PHP_ROUND_HALF_EVEN
PHP_ROUND_HALF_ODD
或者我们真的只限于四舍五入吗?
1。来自 Oracle 文档:
根据MySQL官方文档,舍入函数如下:
For exact-value numbers, ROUND() uses the “round half away from zero” or “round toward nearest” rule: A value with a fractional part of .5 or greater is rounded up to the next integer if positive or down to the next integer if negative. (In other words, it is rounded away from zero.) A value with a fractional part less than .5 is rounded down to the next integer if positive or up to the next integer if negative.
http://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html#function_round
也就是说我们只能使用四舍五入函数来向上取整。我编写了以下 UDF 来解决此限制。
2。代码(在MySQL 5.6中测试):
CREATE FUNCTION roundHalf (
numberToRound DECIMAL(20,6),
roundingPrecision TINYINT(2),
roundingType ENUM (
'ROUND_HALF_UP',
'ROUND_HALF_DOWN',
'ROUND_HALF_EVEN',
'ROUND_HALF_ODD'
)
)
RETURNS DECIMAL(20,6)
BEGIN
DECLARE digitEvenOdd TINYINT (2) UNSIGNED DEFAULT 255;
DECLARE digitPosition TINYINT (2) UNSIGNED DEFAULT 0;
DECLARE digitToRound TINYINT (2) DEFAULT -1;
DECLARE roundedNumber DECIMAL(20,6) DEFAULT 0;
SET digitPosition = INSTR(numberToRound, '.');
IF (roundingPrecision < 1) THEN
SET digitPosition = digitPosition + roundingPrecision;
ELSE
SET digitPosition = digitPosition + roundingPrecision + 1;
END IF;
IF (digitPosition > 0 AND
digitPosition <= CHAR_LENGTH(numberToRound)
) THEN
SET digitToRound = CAST(
SUBSTR(
numberToRound,
digitPosition,
1
) AS UNSIGNED
);
SET digitPosition = digitPosition - 1;
IF (digitPosition > 0 AND
digitPosition <= CHAR_LENGTH(numberToRound)
) THEN
SET digitEvenOdd = CAST(
SUBSTR(
numberToRound,
digitPosition,
1
) AS UNSIGNED
);
END IF;
END IF;
IF (digitToRound > -1) THEN
CASE roundingType
WHEN 'ROUND_HALF_UP' THEN
IF (digitToRound >= 5) THEN
SET roundedNumber = ROUND(numberToRound, roundingPrecision);
ELSE
SET roundedNumber = TRUNCATE(numberToRound, roundingPrecision);
END IF;
WHEN 'ROUND_HALF_DOWN' THEN
IF (digitToRound > 5) THEN
SET roundedNumber = ROUND(numberToRound, roundingPrecision);
ELSE
SET roundedNumber = TRUNCATE(numberToRound, roundingPrecision);
END IF;
WHEN 'ROUND_HALF_EVEN' THEN
IF (digitToRound >= 5 AND
digitEvenOdd IN (1,3,5,7,9)
) THEN
SET roundedNumber = ROUND(numberToRound, roundingPrecision);
ELSE
SET roundedNumber = TRUNCATE(numberToRound, roundingPrecision);
END IF;
WHEN 'ROUND_HALF_ODD' THEN
IF (digitToRound >= 5 AND
digitEvenOdd IN (0,2,4,6,8)
) THEN
SET roundedNumber = ROUND(numberToRound, roundingPrecision);
ELSE
SET roundedNumber = TRUNCATE(numberToRound, roundingPrecision);
END IF;
END CASE;
ELSEIF (roundingPrecision > 0) THEN
SET roundedNumber = numberToRound;
END IF;
RETURN roundedNumber;
END //
注意:四舍五入的数值可以根据项目的需要增加到总共65位(在这种情况下,不要忘记更改所有DECIMAL(20,6)
相应的实例)。
3。舍入结果为 PHP:
http://sandbox.onlinephpfunctions.com/code/054de06b074c2b3ece5fb6e5d4180524cd2207e2
4。单元测试(在 SQL 中):
/* round number not enough digits */
IF (roundHalf(1.455, 7, 'ROUND_HALF_UP') <> 1.455) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_ODD_TEST_1';
ELSEIF (roundHalf(1.455, -5, 'ROUND_HALF_UP') <> 0) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_ODD_TEST_2';
ELSEIF (roundHalf(555, -1, 'ROUND_HALF_UP') <> 560) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_ODD_TEST_3';
END IF;
/* round half up */
IF (roundHalf(1.541, 2, 'ROUND_HALF_UP') <> 1.54) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_UP_TEST_1';
ELSEIF (roundHalf(1.545, 2, 'ROUND_HALF_UP') <> 1.55) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_UP_TEST_2';
ELSEIF (roundHalf(555, 0, 'ROUND_HALF_UP') <> 555) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_UP_TEST_3';
ELSEIF (roundHalf(1000999, -2, 'ROUND_HALF_UP') <> 1001000) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_UP_TEST_4';
ELSEIF (roundHalf(1000999, -3, 'ROUND_HALF_UP') <> 1001000) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_UP_TEST_5';
ELSEIF (roundHalf(1000999, -4, 'ROUND_HALF_UP') <> 1000000) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_UP_TEST_6';
END IF;
/* round half down */
IF (roundHalf(1.541, 2, 'ROUND_HALF_DOWN') <> 1.54) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_DOWN_TEST_1';
ELSEIF (roundHalf(1.545, 2, 'ROUND_HALF_DOWN') <> 1.54) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_DOWN_TEST_2';
ELSEIF (roundHalf(555, 0, 'ROUND_HALF_DOWN') <> 555) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_DOWN_TEST_3';
ELSEIF (roundHalf(1000999, -2, 'ROUND_HALF_DOWN') <> 1001000) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_UP_TEST_4';
ELSEIF (roundHalf(1000999, -3, 'ROUND_HALF_DOWN') <> 1001000) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_DOWN_TEST_5';
ELSEIF (roundHalf(1000999, -4, 'ROUND_HALF_DOWN') <> 1000000) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_DOWN_TEST_6';
END IF;
/* round half even */
IF (roundHalf(1.541, 2, 'ROUND_HALF_EVEN') <> 1.54) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_EVEN_TEST_1';
ELSEIF (roundHalf(1.544, 2, 'ROUND_HALF_EVEN') <> 1.54) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_EVEN_TEST_2';
ELSEIF (roundHalf(1.455, 2, 'ROUND_HALF_EVEN') <> 1.46) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_EVEN_TEST_3';
END IF;
/* round half odd */
IF (roundHalf(1.544, 2, 'ROUND_HALF_ODD') <> 1.54) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_ODD_TEST_1';
ELSEIF (roundHalf(1.545, 2, 'ROUND_HALF_ODD') <> 1.55) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_ODD_TEST_2';
ELSEIF (roundHalf(1.455, 2, 'ROUND_HALF_ODD') <> 1.45) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_ODD_TEST_3';
END IF;
随意使用该编码,但不要忘记喜欢我的post。谢谢大家的意见和建议。
可以使用 ROUND
函数在一行中完成四舍五入:
SELECT ROUND(ROUND({your_input_value}*2,0)/2,1);
- 第一个
ROUND({your_input_value}*2,0)
为您提供一个四舍五入的整数值。 - 第二个 ROUND(.../2,1) 为您提供一位数结果,而不是更长一点的结果,其中包含更多零和可能的浮点偏差。
可以创建具有向上舍入、向下舍入或其他乘数(如 3,4 等)的类似其他回合。
MySQL 中的 ROUND()
函数在相当长一段时间内存在一个错误,它 returns 不一致的结果取决于它是否认为输入值是四舍五入 精确或近似数字。至少记录了几个错误(参见 here as well as MySQL 5.7 description of the behavour here)。
我遇到了 MySQL 5.6.22 的问题,但从 2001 年的一些讨论来看,它似乎相当普遍。它似乎已在 2017 年底和 2018 年初得到修复,但我不能证明哪些版本可以正常工作,哪些不能。
以下是一种解决方法,无论 <input_value>
是否被 MySQL 视为 exact[=35=,都可以使结果一致地保留一位小数] 或 近似值 数:
SELECT ROUND(CAST(<input_value> AS DECIMAL(10,2)), 1)
如果需要四舍五入到更多的地方,将第二个参数ROUND()
从1
改为你需要的小数位数,将第二个参数从DECIMAL()
改为DECIMAL()
2
设置为比您需要的小数位数高一位的值。例如。对于小数点后 3 位,您可以使用:
SELECT ROUND(CAST(<input_value> AS DECIMAL(10,4)), 3)
请注意,DECIMAL()
的第一个参数是数字中的总位数,因此,如果您需要存储较大的值,则可能需要相应地进行调整。有关详细信息,请参阅 MySQL manual。
如果您在应用@Jonathan 的函数时遇到错误,请说:
Error 1418: This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration and binary logging is enabled (you *might* want to use the less safe log_bin_trust_function_creators variable) SQL Statement:...
您应该在 BEGIN
DETERMINISTIC
或其他适用的声明
DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration and binary logging is enabled