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

Why does DBI implicitly change integers to strings?

我有一个 MySQL table 具有以下结构。

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

从 table 中选择数据后,我得到所有引用的数据并作为字符串值。

#!/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 将所有整数隐式转换为字符串?

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'
        ];

正如@choroba 在他的回答中指出的那样,不是 DBI 在对数据进行任何操作。它只是通过驱动程序模块(DBD::mysql 在你的情况下)returned.

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

Most data is returned to the Perl script as strings. (Null values are returned as undef.) This allows arbitrary precision numeric data to be handled without loss of accuracy. Beware that Perl may not preserve the same accuracy when the string is used as a number.

我在配置 perl 以支持 64 位整数和 long-double 浮点类型很不常见之前的日子里写过。这些天我建议驱动程序 return 值最 'natural' Perl 类型,不会有数据丢失的风险。

对于某些可能难以实现的驱动程序,尤其是那些支持从单个句柄 returning 具有不同列数的多个结果集的驱动程序,如 DBD::mysql 所做的那样。

我浏览了 DBD::mysql docs but didn't see any mention of this topic, so I looked at the relevant code where I can see that the current DBD::mysql is returning numbers as numbers. There's also lots of references to recent changes in this area in the Change log

也许您使用的是旧版本 DBD::mysql,应该升级。

DBI 通常不会这样做。正如已经指出的那样,DBI 系统的许多数据库驱动程序 (DBD::xy) 将数字转换为字符串。据我所知,这是不可能避免的。

您可以做的是询问相应本机类型的语句句柄,或者(在您的情况下更容易)结果集的列在 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':

Reference to an array of boolean values; TRUE indicates, that the respective column contains numeric values. (from 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);

希望对您有所帮助。由于写的仓促,风格还请见谅。当然,我愿意接受任何建议。