今天遇到一个很奇怪的问题,在MySQL客户端输入,用不同科学计数法表示的数值,展示效果却截然不同:
mysql> select 1e+14,1e+15;
+-----------------+-------+
| 1e+14 | 1e+15 |
+-----------------+-------+
| 100000000000000 | 1e15 |
+-----------------+-------+
为什么都是用科学计数法,一个是用完全展开的形式表示,另外一个却变成用科学计数法来表示?
我们知道,在MySQL中解析这类科学计数法的标识token,是通过BISON来进行词法和语法解析的,并最终转成Item类型,Item构造初始化的堆栈如下所示:
#0 Item_float::init (this=0x7fffe844e8c0, str_arg=0xe7f312c00e0f8 <error: Cannot access memory at address 0xe7f312c00e0f8>, length=21845) at /home/greatdb/sql/item.cc:7216
#1 0x0000555559260095 in Item_float::Item_float (this=0x7fff2c00dad8, pos=..., str_arg=0x7fff2c00dac8 "1.234e-14", length=9) at /home/greatdb/sql/item.h:5237
#2 0x00005555592503af in MYSQLparse (YYTHD=0x7fff2c001040, parse_tree=0x7fffe84506b0) at /home/greatdb/sql/sql_yacc.yy:16616
#3 0x0000555558ea2410 in THD::sql_parser (this=0x7fff2c001040) at /home/greatdb/sql/sql_class.cc:3149
#4 0x0000555558fe8126 in parse_sql (thd=0x7fff2c001040, parser_state=0x7fffe8450990, creation_ctx=0x0) at /home/greatdb/sql/sql_parse.cc:7391
#5 0x0000555558fe1f16 in dispatch_sql_command (thd=0x7fff2c001040, parser_state=0x7fffe8450990, update_userstat=false) at /home/greatdb/sql/sql_parse.cc:5293
#6 0x0000555558fd7969 in dispatch_command (thd=0x7fff2c001040, com_data=0x7fffe8451b70, command=COM_QUERY) at /home/greatdb/sql/sql_parse.cc:1994
#7 0x0000555558fd5cd9 in do_command (thd=0x7fff2c001040) at /home/greatdb/sql/sql_parse.cc:1442
#8 0x00005555591fffc6 in handle_connection (arg=0x555560992ff0) at /home/greatdb/sql/conn_handler/connection_handler_per_thread.cc:307
#9 0x000055555ae2314b in pfs_spawn_thread (arg=0x5555608507a0) at /home/greatdb/storage/perfschema/pfs.cc:2899
#10 0x00007ffff77c3609 in start_thread (arg=<optimized out>) at pthread_create.c:477
#11 0x00007ffff76e8133 in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95
我们仔细看下面这个函数的实现:
void Item_float::init(const char *str_arg, uint length) {
int error;
const char *end_not_used;
//经过观察,我们发现:不管是1e+14还是1e+15,这里把字符串转成浮点数,都是完全展开形式。
//即1e+14展开为100000000000000
//1e+15展开为1000000000000000
//说明至少到目前为止,没有触发展示差异的原因。
value = my_strntod(&my_charset_bin, str_arg, length, &end_not_used, &error);
if (error) {
char tmp[NAME_LEN + 1];
snprintf(tmp, sizeof(tmp), "%.*s", length, str_arg);
my_error(ER_ILLEGAL_VALUE_FOR_TYPE, MYF(0), "double", tmp);
}
presentation.copy(str_arg, length);
item_name.copy(str_arg, length);
set_data_type(MYSQL_TYPE_DOUBLE);
decimals = (uint8)nr_of_decimals(str_arg, str_arg + length);
max_length = length;
fixed = true;
}
接着我们继续查看Item处理结果的堆栈信息:
#0 my_gcvt (x=100000000000000, type=MY_GCVT_ARG_DOUBLE, width=342, to=0x7fffe844ec60 "\033", error=0x0) at /home/greatdb/strings/dtoa.cc:428
#1 0x00005555594ba4d5 in floating_point_to_text (value=100000000000000, decimals=31, gcvt_arg_type=MY_GCVT_ARG_DOUBLE, buffer=0x7fffe844ec60 "\033") at /home/greatdb/sql/protocol_classic.cc:3539
#2 0x00005555594ba53d in store_floating_point (value=100000000000000, decimals=31, zerofill=0, gcvt_arg_type=MY_GCVT_ARG_DOUBLE, packet=0x7fff2c003148) at /home/greatdb/sql/protocol_classic.cc:3558
#3 0x00005555594ba703 in Protocol_text::store_double (this=0x7fff2c005600, from=100000000000000, decimals=31, zerofill=0) at /home/greatdb/sql/protocol_classic.cc:3579
#4 0x0000555558aa2260 in Item::send (this=0x7fff2c021f90, protocol=0x7fff2c005600, buffer=0x7fffe844ef00) at /home/greatdb/sql/item.cc:7625
#5 0x0000555558ea1886 in THD::send_result_set_row (this=0x7fff2c001040, row_items=const mem_root_deque<Item*> & = {...}) at /home/greatdb/sql/sql_class.cc:2975
#6 0x0000555558ddbf7d in Query_result_send::send_data (this=0x7fff2c022c88, thd=0x7fff2c001040, items=const mem_root_deque<Item*> & = {...}) at /home/greatdb/sql/query_result.cc:109
#7 0x000055555912c6e3 in Query_expression::ExecuteIteratorQuery (this=0x7fff2c021408, thd=0x7fff2c001040) at /home/greatdb/sql/sql_union.cc:1282
#8 0x000055555912ca74 in Query_expression::execute (this=0x7fff2c021408, thd=0x7fff2c001040) at /home/greatdb/sql/sql_union.cc:1342
#9 0x0000555559069670 in Sql_cmd_dml::execute_inner (this=0x7fff2c022c50, thd=0x7fff2c001040) at /home/greatdb/sql/sql_select.cc:827
#10 0x00005555590689ec in Sql_cmd_dml::execute (this=0x7fff2c022c50, thd=0x7fff2c001040) at /home/greatdb/sql/sql_select.cc:580
#11 0x0000555558fe0324 in mysql_execute_command (thd=0x7fff2c001040, first_level=true) at /home/greatdb/sql/sql_parse.cc:4788
#12 0x0000555558fe24bb in dispatch_sql_command (thd=0x7fff2c001040, parser_state=0x7fffe8450990, update_userstat=false) at /home/greatdb/sql/sql_parse.cc:5393
#13 0x0000555558fd7969 in dispatch_command (thd=0x7fff2c001040, com_data=0x7fffe8451b70, command=COM_QUERY) at /home/greatdb/sql/sql_parse.cc:1994
#14 0x0000555558fd5cd9 in do_command (thd=0x7fff2c001040) at /home/greatdb/sql/sql_parse.cc:1442
#15 0x00005555591fffc6 in handle_connection (arg=0x555560992ff0) at /home/greatdb/sql/conn_handler/connection_handler_per_thread.cc:307
#16 0x000055555ae2314b in pfs_spawn_thread (arg=0x5555608507a0) at /home/greatdb/storage/perfschema/pfs.cc:2899
#17 0x00007ffff77c3609 in start_thread (arg=<optimized out>) at pthread_create.c:477
#18 0x00007ffff76e8133 in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95
我们发现不管是1e+14还是1e+15,最终都会进入到my_gcvt这个函数里面,此函数大概的功能是把一个浮点数类型转成对应的字符串类型输出。现在我们来看该函数的关键逻辑实现:
//1、x表示要转换的浮点数
//2、width表示展示宽度。具体来说:MySQL其实对数据库返回的每一个列的最大宽度是有要求的。如果浮点数是完全展开的情况,那就有可能超过这个展示的最大宽度,这里固定取值342
//3、to表示存放转换后浮点数x对应的字符数组指针
size_t my_gcvt(double x, my_gcvt_arg_type type, int width, char *to,
bool *error) {
int decpt, sign, len, exp_len;
char *res, *src, *end, *dst = to, *dend = dst + width;
char buf[DTOA_BUFF_SIZE];
bool have_space, force_e_format;
assert(width > 0 && to != nullptr);
...
//这里主要是为了计算浮点数x的存储的小数点位置decpt。
//比如100000000000000,得到的decpt为15
//1000000000000000,得到的decpt为16
res =
dtoa(x, 4, type == MY_GCVT_ARG_DOUBLE ? width : std::min(width, FLT_DIG),
&decpt, &sign, &end, buf, sizeof(buf));
...
//have_space为真表示,浮点数完全展开的形式对应的长度,符合MySQL列展示最大宽度的要求。
//下面这个判断的逻辑主要是说:
//浮点数的展示宽度足够或者展示宽度不足的情况下,继续判断小数点位置decpt是否介于[-MAX_DECPT_FOR_F_FORMAT + 1,MAX_DECPT_FOR_F_FORMAT]之间。
//跟踪一下代码的宏定义,很容易发现MAX_DECPT_FOR_F_FORMAT定义的长度就是15。
//那这样就是说decpt介于[-14,15]之间的时候,也是符合要求的。
if ((have_space ||
/*
Not enough space, let's see if the 'f' format provides the most number
of significant digits.
*/
((decpt <= width &&
(decpt >= -1 || (decpt == -2 && (len > 1 || !force_e_format)))) &&
!force_e_format)) &&
/*
Use the 'e' format in some cases even if we have enough space for the
'f' one. See comment for MAX_DECPT_FOR_F_FORMAT.
*/
(!have_space || (decpt >= -MAX_DECPT_FOR_F_FORMAT + 1 &&
(decpt <= MAX_DECPT_FOR_F_FORMAT || len > decpt)))) {
//如果符合这个条件的要求,那浮点数x就按照'f' format,即非科学计数法,完全展开形式处理。
//1e+14的decpt取值为15,介于[-14,15]区间,故按照完全展开形式处理。
...
}else{
//否则浮点数x按照'e' format,即科学计数法表示。
//1e+15的decpt取值为16,超出[-14,15]区间,故按照科学计数法形式处理。
...
}
}
经过代码的调用分析,发现最终的结论和输入数据的现象相符。当我们在使用MySQL过程中,遇到问题的时候,不要慌乱。可以尝试从源码分析的角度作为切入点,从根源上理解这种现象触发的原因,更能进一步加深我们对数据库运行机制的了解和掌握。
Enjoy GreatSQL ?
GreatSQL是由万里数据库维护的MySQL分支,专注于提升MGR可靠性及性能,支持InnoDB并行查询特性,是适用于金融级应用的MySQL分支版本。
相关链接: GreatSQL社区 Gitee GitHub Bilibili
捉虫活动详情:https://greatsql.cn/thread-97-1-1.html
社区博客有奖征稿详情:https://greatsql.cn/thread-100-1-1.html

微信:扫码添加
GreatSQL社区助手微信好友,发送验证信息加群。
)
尝试在我的RoR应用程序中实现计数器缓存列时出现错误Unknownkey(s):counter_cache。我在这个问题中实现了模型关联:Modelassociationquestion这是我的迁移:classAddVideoVotesCountToVideos0Video.reset_column_informationVideo.find(:all).eachdo|p|p.update_attributes:videos_votes_count,p.video_votes.lengthendenddefself.downremove_column:videos,:video_vot
我正在尝试按0-9和a-z的顺序创建数字和字母列表。我有一组值value_array=['0','1','2','3','4','5','6','7','8','9','a','b','光盘','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','','u','v','w','x','y','z']和一个组合列表的数组,按顺序,这些数字可以产生x个字符,比方说三个list_array=[]和一个当前字母和数字组合的数组(在将它插入列表数组之前我会把它变成一个字符串,]current_combo['0','0','0']
文章目录一、概述简介原理模块二、配置Mysql使用版本环境要求1.操作系统2.mysql要求三、配置canal-server离线下载在线下载上传解压修改配置单机配置集群配置分库分表配置1.修改全局配置2.实例配置垂直分库水平分库3.修改group-instance.xml4.启动监听四、配置canal-adapter1修改启动配置2配置映射文件3启动ES数据同步查询所有订阅同步数据同步开关启动4.验证五、配置canal-admin一、概述简介canal是Alibaba旗下的一款开源项目,Java开发。基于数据库增量日志解析,提供增量数据订阅&消费。Git地址:https://github.co
我看到其他人也遇到过类似的问题,但没有一个解决方案对我有用。0.3.14gem与其他gem文件一起存在。我已经完全按照此处指示完成了所有操作:https://github.com/brianmario/mysql2.我仍然得到以下信息。我不知道为什么安装程序指示它找不到include目录,因为我已经检查过它存在。thread.h文件存在,但不在ruby目录中。相反,它在这里:C:\RailsInstaller\DevKit\lib\perl5\5.8\msys\CORE\我正在运行Windows7并尝试在Aptana3中构建我的Rails项目。我的Ruby是1.9.3。$gemin
我已经开始使用mysql2gem。我试图弄清楚一些基本的事情——其中之一是如何明确地执行事务(对于批处理操作,比如多个INSERT/UPDATE查询)。在旧的ruby-mysql中,这是我的方法:client=Mysql.real_connect(...)inserts=["INSERTINTO...","UPDATE..WHEREid=..",#etc]client.autocommit(false)inserts.eachdo|ins|beginclient.query(ins)rescue#handleerrorsorabortentirelyendendclient.commi
我有一个数组:array=['Footballs','Baseball','football','Soccer']而且我需要计算看到Football或Baseball的次数,无论大小写和复数形式如何。这是我尝试做的,但没有成功:array.count{|x|x.downcase.include?'football'||x.downcase.include?'baseball'}编写这段代码的正确或更好的方法是什么?我正在寻找3作为答案。 最佳答案 我会将count与一个block结合使用,该block根据与您正在寻找的约束相匹配的正
我遇到了同样的问题here对于python,但对于ruby。我需要输出这样一个小数字:0.00001,而不是1e-5。有关我的特定问题的更多信息,我正在使用f.write("Mynumber:"+small_number.to_s+"\n")输出到一个文件对于我的问题,准确性不是什么大问题,所以只做一个if语句来检查是否small_number那么更通用的方法是什么? 最佳答案 f.printf"Mynumber:%.5f\n",small_number您可以将.5(小数点右侧5位数字)替换为您喜欢的任何特定格式大小,例如,%8
我正在尝试绕过rails配置这个极其复杂的迷宫。到目前为止,我设法在ubuntu上设置了rvm(出于某种原因,ruby在ubuntu存储库中已经过时了)。我设法建立了一个Rails项目。我希望我的测试项目使用mysql而不是mysqlite。当我尝试“rakedb:migrate”时,出现错误:“!!!缺少mysql2gem。将其添加到您的Gemfile:gem'mysql2'”当我尝试“geminstallmysql”时,出现错误,告诉我需要为安装命令提供参数。但是,参数列表很大,我不知道该选择哪些。如何通过在ubuntu上运行的rvm和mysql获取rails3?谢谢。
我正在为在AmazonEC2实例上运行的应用程序设计一个AutoScaling系统。应用程序从SQS读取消息并对其进行处理。AutoScaling系统将监控两件事:SQS中的消息数量,所有EC2机器上运行的进程总数。例如,如果SQS中的消息数量超过3000,我希望系统自动缩放,创建一个新的EC2实例,在其上部署代码,当消息数量低于2000时,我希望系统终止EC2实例.我正在用Ruby和Capistrano做这件事。我的问题是:我无法找到一种方法来确定在所有EC2机器上运行的进程数并将该数字保存在变量中。你能帮帮我吗? 最佳答案 您可
我有以下工厂:FactoryGirl.definedofactory:foodosequence(:name){|n|"Foo#{n}"}trait:ydosequence(:name){|n|"Fooy#{n}"}endendend如果我跑create:foocreate:foocreate:foo,:y我得到Foo1,Foo2,Fooy1。但我想要Foo1,Foo2,Fooy3。我怎样才能做到这一点? 最佳答案 经过smile2day'sanswer的一些提示后和thisanswer,我得出以下解决方案:FactoryGirl.