长整数在插入较短的列时进行转换,而不是截断。为啥?公式是啥?

Posted

技术标签:

【中文标题】长整数在插入较短的列时进行转换,而不是截断。为啥?公式是啥?【英文标题】:Long integer is transformed when inserted in shorter column, not truncated. Why? What is the formula?长整数在插入较短的列时进行转换,而不是截断。为什么?公式是什么? 【发布时间】:2014-06-26 08:16:26 【问题描述】:

我有一列 integer 类型的列,长度为 10:

`some_number` int(10) unsigned NOT NULL

我在此列中插入了一个太长的数字:

$some_number = 715988985123857;
$query = "INSERT INTO this_table SET some_number = ?";
$stmt = $mysqli->prepare($query);
$stmt->bind_param('i', $some_number);
$stmt->execute();

当我查看表格中的内容时,现在的数字是:

2147483647

715988985123857如何以及为什么变成2147483647? 为什么它没有被截断? 这种转换背后的机制是什么,能用某种公式计算得到的数字吗?


我不是在寻找解决方案。我只是想了解具体的数字。

【问题讨论】:

【参考方案1】:

http://dev.mysql.com/doc/refman/5.0/en/integer-types.html

整数溢出会将DB中的最大允许数设置为

2147483647

所以你需要bigint 数据类型来存储更大的整数

【讨论】:

准确来说是最大有符号的32位整数。 是的,maximum signed 32-bit integer【参考方案2】:

715988985123857如何以及为什么变成2147483647? 为什么它没有被截断? 这种转换背后的机制是什么,能用某种公式计算得到的数字吗?

行为取决于 MySQL Strict SQL Mode 设置:

严格模式控制 MySQL 如何处理数据更改语句(如 INSERT 或 UPDATE)中的无效或缺失值。

MySQL 手册中有一个章节解释了 MySQL 如何处理超出范围的值:Out-of-Range and Overflow Handling:

[...] 如果没有启用限制模式,MySQL 会将值剪切到范围的适当端点并存储结果值。

Integer Type的情况下,最大值为:

2147483647 用于签名类型 4294967295 用于无符号类型

由于您的查询没有引发错误,而是使用最大允许值更新了该列,因此您可以假设您的服务器上未启用严格 SQL 模式。您可以通过运行来验证:

SELECT @@GLOBAL.sql_mode;
SELECT @@SESSION.sql_mode;

它们都不包含STRICT_TRANS_TABLESSTRICT_ALL_TABLES 值(更多关于MySQL man 中的值:sql_mode)。 以这个例子来测试模式之间的不同行为:

mysql> create table test_sql_mode(id int);
mysql> set sql_mode = 'STRICT_TRANS_TABLES';
mysql> SELECT @@SESSION.sql_mode;
+---------------------+
| @@SESSION.sql_mode  |
+---------------------+
| STRICT_TRANS_TABLES |
+---------------------+
1 row in set (0.00 sec)

mysql> insert into test_sql_mode(id) value(123456789123456789);
ERROR 1264 (22003): Out of range value for column 'id' at row 1

mysql> select * from test_sql_mode;
Empty set (0.00 sec)

mysql> set sql_mode = '';
mysql> SELECT @@SESSION.sql_mode;
+--------------------+
| @@SESSION.sql_mode |
+--------------------+
|                    |
+--------------------+
1 row in set (0.00 sec)

mysql> insert into test_sql_mode(id) value(123456789123456789);
Query OK, 1 row affected, 1 warning (0.05 sec)

mysql> show warnings;
+-------+------+---------------------------------------------+
| Level | Code | Message                                     |
+-------+------+---------------------------------------------+
| Error | 1264 | Out of range value for column 'id' at row 1 |
+-------+------+---------------------------------------------+

mysql> select * from test_sql_mode;
+------------+
| id         |
+------------+
| 2147483647 |
+------------+
1 row in set (0.00 sec)

简而言之,在严格模式下,超出范围的值会产生错误,而没有 - 值会调整到允许的限制并产生警告。


关于这个问题:

可以用一些公式计算得到的数字吗?

我不知道任何简单而纯粹的 MySQL 公式。您必须检查数据类型和长度:

select data_type, numeric_precision, numeric_scale
from information_schema.columns
where table_schema = "test_database"
and table_name = "test_sql_mode"
and column_name = "id"

...并基于此确定允许的限制。

非sql?数据验证(服务器端和客户端)是解决方案。

【讨论】:

【参考方案3】:

据我所知,它与php,bcx没有任何关系。

如果 PHP 遇到超出整数类型界限的数字,它将被解释为浮点数。 此外,导致数字超出整数类型范围的操作将改为返回浮点数。

因此,改变值的将是 SQL 的功能。

【讨论】:

以上是关于长整数在插入较短的列时进行转换,而不是截断。为啥?公式是啥?的主要内容,如果未能解决你的问题,请参考以下文章

MySQL:如何使用 CASE 或 IF 匹配较短的列的长度?

为啥使用整数而不是长整数?

为啥 int 被提升为 double 而不是 float 以进行隐式转换

sql 别名允许您通过用较短的名称(通常是单个字符)替换表的长名称来键入较少的字符。第8行显示

较短的代码是不是有明显的性能优势?

插入时截断的长字符串