简体   繁体   English

perl dbi mysql - 数值精度

[英]perl dbi mysql - value precision

user@host:~# mysql -V - mysql Ver 14.14 Distrib 5.7.25-28, for debian-linux-gnu (x86_64) using 7.0 running under debian-9,9 user@host:~# mysql -V - mysql Ver 14.14 Distrib 5.7.25-28,用于 debian-linux-gnu (x86_64) 使用 7.0 在 debian-9,9 下运行

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 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

user@host:~# perl -MDBI -e 'print $DBI::VERSION."\n";' user@host:~# perl -MDBI -e 'print $DBI::VERSION."\n";' - 1.636 - 1.636

user@host:~# perl -v This is perl 5, version 24, subversion 1 (v5.24.1) built for x86_64-linux-gnu-thread-multi user@host:~# perl -v这是为 x86_64-linux-gnu-thread-multi 构建的 perl 5,版本 24,subversion 1 (v5.24.1)

mysql> SHOW CREATE TABLE tbl1; mysql> SHOW CREATE TABLE tbl1;

tbl1 | CREATE TABLE `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',
  PRIMARY KEY (`id`),
  KEY `main_id` (`main_id`)
  ) ENGINE=InnoDB AUTO_INCREMENT=70013000018275 DEFAULT CHARSET=utf8 

mysql> SELECT * FROM tbl1 WHERE id=70003020040132; mysql> SELECT * FROM tbl1 WHERE id=70003020040132;

-+---------------+----------------+-------+--------+---------+---------+
| id             | main_id        | debet | kurs   | summ    | is_sync | 
+----------------+----------------+-------+--------+---------+---------+
| 70003020040132 | 70003020038511 |       | 0.0000 | 1798.00 | 0       |
+----------------+----------------+-------+--------+---------+---------+

But when I get this data by perl::DBI module I lose precisions, and values 0.0000 and 1798.00 becomes 0 and 1798 .但是当我通过 perl::DBI 模块获得这些数据时,我失去了精度,值0.00001798.00变为01798

Code is next:接下来是代码:

#### 
#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";

and as a result I get next string:结果我得到下一个字符串:

REPLACE INTO `pko_plat` (`id`, `main_id`, `debet`, `kurs`, `summ`, `is_sync`) VALUES  ('70003020040132', '70003020038511', '', '0', '1798', '0')

in DB it was 0.0000 - become 0 , was 1798.00 , become 1798在 DB 中它是0.0000 - 变成0 ,是1798.00 ,变成1798

Perl's DBI documentation says it gets data 'as is' into strings, no translations made. Perl 的 DBI 文档说它将数据“按原样”转换为字符串,不进行任何翻译。 But, then, who rounded values?但是,那么,谁对价值进行了四舍五入呢?

The rounding you see is happening because of the way you create the columns.由于您创建列的方式,您看到的舍入正在发生。

  `kurs` double(20,4) NOT NULL DEFAULT '0.0000'
  `summ` double(20,2) NOT NULL DEFAULT '0.00'

If you look at the mysql floating point type documentation you will see that you are using the non-standard syntax double(m, d) , where the two parameters define how the float is being output.如果您查看mysql 浮点类型文档,您将看到您使用的是非标准语法double(m, d) ,其中两个参数定义了浮点数如何成为 output。

So in your case the values stored in summ will be displayed with 2 digits behind the point.因此,在您的情况下,存储在summ中的值将在该点后面显示 2 位数字。 This means that when perl gets a value out of the table which is 1.0001 in the database, the value that perl gets delivered by the database is rounded to the set number of digits (in this case.00).这意味着,当 perl 从表中获取一个值,即数据库中的 1.0001 时,数据库传递的 perl 得到的值将四舍五入为设置的位数(在本例中为 00)。

Perl in turn interprets this value ("1.00") as a float, and when printed, will not show any trailing zeroes. Perl 反过来将此值(“1.00”)解释为浮点数,打印时不会显示任何尾随零。 If you want these you should accommodate for this in your output.如果你想要这些,你应该在你的 output 中适应这些。

For example: print sprintf("%.2f\n", $summ);例如: print sprintf("%.2f\n", $summ);

The way I see it you have two ways you can go (if you want to avoid this loss of precision):在我看来,你有两种方法可以 go (如果你想避免这种精度损失):

  • Only add numbers with the correct precision to the database (so for 'summ' only two trailing digits, and four for 'kurs' .)仅将具有正确精度的数字添加到数据库中(因此对于'summ'只有两个尾随数字,对于'kurs'是四个。)
  • Alter your table creation to the standard syntax for floats and determine the output formatting in Perl (which you will be doing either way):将您的表创建更改为浮点的标准语法,并确定 Perl 中的 output 格式(您将采用任何一种方式):
`kurs` double() NOT NULL DEFAULT '0.0'

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM