为什么DBI会将整数隐式更改为字符串?

时间:2016-11-25 08:01:49

标签: mysql perl dbi

我有一个具有以下结构的MySQL表。

alid       bigint(20),
ndip       varchar(20),
ndregion   varchar(20),
occ_num    int(3),
Delta_Flag int(1)

从表中选择数据后,我将获取所有引用的数据并作为字符串值。

#!/usr/bin/perl

use strict;
use warnings;

use Data::Dumper;
use FindBin;
use lib $FindBin::Bin;
use Database;

my $pwd = $FindBin::Bin;

my $db  = Database->new( 'mysql', "$pwd/config.ini" );
my $db1 = Database->new( 'mysql', "$pwd/config2.ini" );

my @tables = qw( AutoTT_AlarmStatus_Major1 );

for my $table ( @tables ) {

    my $query_select = "SELECT alid, ndip, ndregion, occ_num, Delta_Flag FROM $table LIMIT 1";
    my $result = $db->db_get_results( $query_select );

    print Dumper( $result );

    for my $item ( @{$result} ) {

        # Here I want to prepare, bind and insert this data
        # into other table with same structure
    }
}

Database.pm

sub db_get_results {
    my $self = shift;
    my $qry  = shift;

    my $sth  = $self->{dbh}->prepare( $qry );
    $sth->execute();

    my @return = ();
    while ( my @line = $sth->fetchrow_array ) {
        push @return, \@line;
    }

    return \@return;
}

输出:

$VAR1 = [
          [
            '1788353',
            '10.34.38.12',
            'North Central',
            '1',
            '1'
          ]
        ];

为什么DBI隐式将所有整数转换为字符串?

3 个答案:

答案 0 :(得分:6)

正如@choroba在他的回答中指出的那样,不是DBI正在对数据做任何事情。它只是通过驱动程序模块(在您的情况下为DBD :: mysql)返回。

在DBI文档的General Interface Rules & Caveats部分中,它说:

  

大多数数据作为字符串返回到Perl脚本。 (空值返回为undef。)这允许处理任意精度数值数据而不会损失准确性。请注意,当字符串用作数字时,Perl可能无法保持相同的准确度。

我在将perl配置为支持64位整数的常见日子之前写了这篇文章,并且长双浮点类型不常见。这些天我建议驱动程序返回最“自然”Perl类型的值,不会有数据丢失的风险。

对于某些可能很难实现的驱动程序,特别是那些支持从单个句柄返回多个结果集的数据集,如DBD :: mysql那样。

我浏览了DBD::mysql docs,但没有看到任何关于这个主题的提及,所以我看了the relevant code我可以看到当前的DBD :: mysql 将数字作为数字返回。还有很多references to recent changes in this area in the Change log

也许您正在使用旧版本的DBD :: mysql并且应该升级。

答案 1 :(得分:1)

这就是MySQL的DBD驱动程序的工作方式。其他数据库可能表现不同。例如,在SQLite中,数字仍为数字:

#!/usr/bin/perl
use warnings;
use strict;

use DBI;
use Data::Dumper; 

my $dbh = 'DBI'->connect('dbi:SQLite:dbname=:memory:', q(), q());
$dbh->do('CREATE TABLE t (id INT, val VARCHAR(10))');

my $insert = $dbh->prepare('INSERT INTO t VALUES (?, ?)');
$insert->execute(@$_) for [ 1, 'foo' ], [ 2, 'bar' ];

my $query = $dbh->prepare('SELECT id, val FROM t');
$query->execute;
while (my $row = $query->fetchrow_arrayref) {
    print Dumper($row);
}

__END__
$VAR1 = [
          1,
          'foo'
        ];
$VAR1 = [
          2,
          'bar'
        ];

答案 2 :(得分:0)

一般来说,这不是DBI所做的。正如已经指出的那样,DBI系统的许多数据库驱动程序(DBD :: xy)将数字转换为字符串。 AFAIK无法避免这种情况。

你可以做的是询问语句句柄是否有相应的本机类型,或者(在你的情况下更容易),你的结果集列是否是mysql-DB中的数字。这是一个例子

鉴于此基本数据库:

mysql> create table test (id INT,text VARCHAR(20));
Query OK, 0 rows affected (0.01 sec)

mysql> INSERT INTO test VALUES (1,'lalala');
Query OK, 1 row affected (0.00 sec)

您可以使用特定于驱动程序的字段' mysql_is_num'来查找列是否为数字:

  

引用布尔值数组; TRUE表示相应列包含数值。   (来自DBD::mysql

#!/usr/bin/env perl 

use strict;
use warnings;
use utf8;

use DBI;
use DBD::mysql;
use Data::Dumper;

my $dsn = "DBI:mysql:database=test;host=localhost";
my $dbh = DBI->connect($dsn,'user','pass') or die "$!";
my $sql = "SELECT * FROM test WHERE id = ?";
my $sth = $dbh->prepare($sql);

$sth->execute(1);

my $num_fields = $sth->{'NUM_OF_FIELDS'};
my $num_mask = $sth->{'mysql_is_num'};

my $result;
my $cnt = 0;
while (my $line = $sth->fetchrow_arrayref){
    for (my $i = 0; $i < $num_fields; $i++){
        if ($num_mask->[$i]){
            $line->[$i] + 0;
        }
        $result->[$cnt] = $line;
    }
    $cnt++;    
}

print Dumper($result);

我希望这会有所帮助。因为它写得很匆忙,请原谅我的风格。当然,我对任何建议持开放态度。