【问题标题】:Why does DBI implicitly change integers to strings?为什么 DABI 隐式将整数转换为字符串?
【发布时间】:2017-04-09 13:44:13
【问题描述】:

我有一个具有以下结构的 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
    }
}

数据库.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 隐式将所有整数转换为字符串?

【问题讨论】:

  • 你知道它在 Perl 中没有任何区别吗?
  • 是的,不是在 Perl 中,但如果我想将此数据插入到具有相同结构的其他表中,那么这是一个问题。我需要明确地进行转换吗?
  • “不在 Perl 中”是什么意思?您可以使用数据按原样使用 Perl 创建新记录。如果您没有使用Data::Dumper 查看数据,那么您会发现很难发现您使用的是字符串还是整数。你为什么不试试呢?
  • @Borodin,虽然在 Perl 中字符串与数字的问题通常无关紧要,但有时确实如此。序列化为 JSON 是一个常见的例子。
  • 你使用的是什么版本的 DBD::mysql?

标签: mysql perl dbi


【解决方案1】:

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

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

大部分数据以字符串形式返回到 Perl 脚本。 (Null 值作为 undef 返回。)这允许在不损失精度的情况下处理任意精度的数值数据。请注意,当字符串用作数字时,Perl 可能无法保持相同的准确性。

早在配置 perl 以支持 64 位整数和 long-double 浮点类型不常见之前,我就写过这句话。这些天来,我建议驱动程序以最“自然”的 Perl 类型返回值,不会有数据丢失的风险。

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

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

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

【讨论】:

  • 是的,我会尝试不同的版本。同样值得注意的是,在 insertionMySQL 期间,数据再次通过 DBI 从字符串转换为预期的数据类型。
【解决方案2】:

这就是 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'
        ];

【讨论】:

  • 一些话会很有帮助。您似乎在证明 SQLite 不同,但这与问题无关。
  • @Borodin:添加了一些词。
【解决方案3】:

这不是 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);

我希望这会有所帮助。由于写的匆忙,请原谅风格。我当然愿意接受任何建议。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-11-02
    • 1970-01-01
    • 2017-01-19
    • 2014-01-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多