mysql - perl dbi mysql - 值精度
问题描述
user@host:~# mysql -V
- mysql Ver 14.14 Distrib 5.7.25-28,适用于使用 7.0 在 debian-9,9 下运行的 debian-linux-gnu (x86_64)
user@host:~# uname -a
- Linux 4.9.0-8-amd64 #1 SMP Debian 4.9.144-3.1 (2019-02-19) x86_64 GNU/Linux
用户@主机:~# perl -MDBI -e 'print $DBI::VERSION ."\n";'
- 1.636
user@host:~# perl -v
这是为 x86_64-linux-gnu-thread-multi 构建的 perl 5, version 24, subversion 1 (v5.24.1)
mysql> SHOW CREATE TABLE tbl1;
表1 | 创建表`tbl1`( `id` bigint(20) NOT NULL AUTO_INCREMENT, `main_id` bigint(20) NOT NULL DEFAULT '0', `debet` varchar(255) NOT NULL DEFAULT '', `kurs` double(20,4) NOT NULL DEFAULT '0.0000', `summ` double(20,2) NOT NULL DEFAULT '0.00', `is_sync` int(11) NOT NULL DEFAULT '0', 主键(`id`), KEY `main_id` (`main_id`) ) 引擎=InnoDB AUTO_INCREMENT=70013000018275 默认字符集=utf8
mysql> SELECT * FROM tbl1 WHERE id=70003020040132;
-+---------------+----------------+--------+------- -+---------+----------+ | 编号 | main_id | 债务 | 库尔斯 | 总结 | is_sync | +----------------+----------------+--------+----- -+---------+----------+ | 70003020040132 | 70003020038511 | | 0.0000 | 1798.00 | 0 | +----------------+----------------+--------+----- -+---------+----------+
但是当我通过 perl::DBI 模块获取这些数据时,我失去了精度和值0.0000
,1798.00
变成了0
and 1798
。
接下来是代码:
####
#These 3 subs are connecting to DB, executing query and get data by fetchall_arrayref and coverting undef to NULL.
####
sub DB_connect {
# DataBase Handler
my $dbh = DBI->connect("DBI:mysql:$DBNAME", $DBUSER, $DBPWD,{RaiseError => 0, PrintError => 0, mysql_enable_utf8 => 1}) or die "Error connecting to database: $DBI::errstr";
return $dbh;
}
sub DB_executeQuery {
# Executes SQL query. Return reference to array, or array, according to argv[0]
# argv[0] - "A" returns array, "R" - reference to array
# argv[1] - DB handler from DB_connect
# argv[2] - query to execute
my $choice=shift @_;
my $dbh=shift @_;
my $query=shift @_;
print "$query\n" if $DEBUG>2;
my $sth=$dbh->prepare($query) or die "Error preparing $query for execution: $DBI::errstr";
$sth->execute;
my $retval = $sth->fetchall_arrayref;
if ($choice eq "A" ) {
my @ret_arr=();
foreach my $value (@{ $retval }) {
push @ret_arr,@{ $value };
}
return @ret_arr;
}
elsif ($choice eq "R") {
return $retval;
}
}
sub undef2null {
# argv[1] - reference ro array of values where undef
# values has to be changed to NULL
# Returns array of prepared values: (...) (...) ...
my $ref=shift @_;
my @array=();
foreach my $row (@{ $ref }) {
my $str="";
foreach my $val ( @{ $row} ) {
if (! defined ( $val )) {
$str="$str, NULL";
}
else {
# Escape quotes and other symbols listed in square brackets
$val =~ s/([\"\'])/\\$1/g;
$str="$str, \'$val\'";
}
}
# Remove ', ' at the beginning of each VALUES substring
$str=substr($str,2);
push @array,"($str)";
} # End foreach my $row (@{ $ref_values })
return @array;
} # End undef2null
#### Main call
#...
# Somewhere in code I get data from DB and print it to out file
my @arr_values=();
my @arr_col_names=DB_executeQuery("A",$dbh,qq(SELECT column_name FROM `information_schema`.`columns` WHERE `table_schema` = '$DBNAME' AND `table_name` = '@{ $table }'));
@arr_ids=DB_executeQuery("A",$dbh,qq(SELECT `id` FROM `@{ $table }` WHERE `is_sync`=0));
my $ref_values=DB_executeQuery("R",$dbh,"SELECT * FROM \`@{ $table }\` WHERE \`id\` IN(".join(",",@arr_ids).")");
@arr_values=undef2null($ref_values);
print FOUT "REPLACE INTO \`@{ $table }\` (`".join("`, `",@arr_col_names)."`) VALUES ".(join ", ",@arr_values).";\n";
结果我得到下一个字符串:
替换为`pko_plat`(`id`,`main_id`,`debet`,`kurs`,`summ`,`is_sync`)值('70003020040132','70003020038511','','0','1798' , '0')
在 DB 中,它0.0000
变成了0
,曾经1798.00
,变成了1798
Perl 的 DBI 文档说它将数据“按原样”转换为字符串,不进行任何翻译。但是,那么,谁对价值进行了四舍五入呢?
解决方案
由于您创建列的方式,您看到的舍入正在发生。
`kurs` double(20,4) NOT NULL DEFAULT '0.0000'
`summ` double(20,2) NOT NULL DEFAULT '0.00'
如果您查看mysql 浮点类型文档,您将看到您使用的是非标准语法double(m, d)
,其中两个参数定义了浮点数的输出方式。
因此,在您的情况下,存储的值summ
将在该点后面显示 2 位数字。这意味着当 perl 从数据库中的表中获取一个值 1.0001 时,perl 获得的数据库传递的值将四舍五入到设定的位数(在本例中为 .00)。
Perl 反过来将此值(“1.00”)解释为浮点数,并且在打印时不会显示任何尾随零。如果你想要这些,你应该在你的输出中适应这个。
例如:print sprintf("%.2f\n", $summ);
在我看来,你有两种方法可以走(如果你想避免这种精度损失):
- 仅将具有正确精度的数字添加到数据库中(因此
'summ'
只有两个尾随数字,四个用于'kurs'
.) - 将您的表创建更改为浮点的标准语法并确定 Perl 中的输出格式(您将采用任何一种方式):
`kurs` double() NOT NULL DEFAULT '0.0'
推荐阅读
- xcode - 无法使用 MediaLibrary 框架读取外部照片库
- html - 如果使用 target="_blank" 将用户重定向到我的网站主页到另一个页面(同一网站),是否必须使用 rel="noreferrer"?
- sql - 在创建 SSIS 目录时;我在代码 PAGE 上失败了
- excel - 防止自动筛选条件从字符串更改为日期
- regex - 如何创建具有多个条件的 RegEx?
- java - 澄清一下这个链表的实现
- python - ^ 不支持的操作数类型:“字节数组”和“int”(按位运算符)
- python - 面对 chromedriver 意外退出。状态代码为:127 在 AWS ec2 上使用带有 docker 和 python 3.7 的 Headless chrome 浏览器
- sql - 解决“cannot perform a DML operation inside a query”
- python - 如何使用 python PIL 获取图像中的一组颜色