如何告诉DBD :: CSV使用逗号作为小数分隔符?

sim*_*que 4 csv perl dbi

我正在尝试使用带有DBI和DBD :: CSV的德式CSV文件.反过来,这使用Text :: CSV来解析文件.我想使用SQL查询该文件中的数据.

我们先来看一下这个文件.它由分号(;)分隔,其中的数字如下所示:5,23相当于英语5.23.

这是我到目前为止所得到的:

use strict; use warnings;
use DBI;

# create the database handle
my $dbh = DBI->connect(
  'dbi:CSV:',
  undef, undef,
  {
    f_dir => '.',
    f_schema => undef,
    f_ext => '.csv',
    f_encoding => 'latin-1',
    csv_eol => "\n",
    csv_sep_char => ';',
    csv_tables => {
      foo => {
        file => 'foo.csv',
        #skip_first_row => 0,
        col_names => [ map { "col$_" } (1..3)  ], # see annotation below
      },
    },
  },
) or croak $DBI::errstr;

my $sth = $dbh->prepare(
  'SELECT col3 FROM foo WHERE col3 > 80.50 ORDER BY col3 ASC'
);
$sth->execute;

while (my $res = $sth->fetchrow_hashref) {
  say $res->{col3};
}
Run Code Online (Sandbox Code Playgroud)

现在,这看起来很不错.问题是SQL(意思是SQL :: Statement,它位于DBI和DBD :: CSV的某个地方)并不考虑数据col3,这是一个浮点值,中间有一个逗号,作为浮动.相反,它将列视为整数,因为它不理解逗号.

这是一些示例数据:

foo;foo;81,90
bar;bar;80,50
baz;baz;80,70
Run Code Online (Sandbox Code Playgroud)

所以带有这些数据的上述代码将产生一行输出:81,90.当然,这是错误的.它使用了比较的int()部分col3,这是正确的,但不是我想要的.

问题:如何告诉它用逗号处理数字为浮点数?

我想过的事情:

  • 我没有在Text :: CSV中找到任何内置方式来执行此操作.我不确定在Text :: CSV中我可以将其挂钩,或者如果Text :: CSV中有一个机制可以将这些内容放入其中.
  • 我不知道它是否会造成DBD :: CSV想要使用Text :: CSV_XS的问题.
  • 也许我可以在数据被读取并且已经存储在某个地方之后再进行,但我还不确定正确的接入点在哪里.
  • 我知道这些东西存储在SQL :: Statement中.我还不知道在哪里.这在某种程度上可能很方便.

不能选择将源CSV文件更改为带点而不是逗号.

我愿意接受各种建议.通过SQL的其他方法也很受欢迎.非常感谢.

Bor*_*din 13

您需要使用SQL::Statement::Functions(已作为其中一部分加载DBD::CSV)编写用户定义的函数.

这个程序做你想要的.添加0.0到转换后的字符串是绝对不必要的,但它说明了子程序的用途.(另请注意调用f_encoding参数中的拼写错误connect.)

use strict;
use warnings;

use DBI;

my $dbh = DBI->connect(
  'dbi:CSV:',
  undef, undef,
  {
    f_dir => '.',
    f_schema => undef,
    f_ext => '.csv',
    f_encoding => 'latin-1',
    csv_eol => "\n",
    csv_sep_char => ';',
    csv_tables => {
      foo => {
        file => 'test.csv',
        #skip_first_row => 0,
        col_names => [ map { "col$_" } (1..3)  ], # see annotation below
      },
    },
  },
) or croak $DBI::errstr;

$dbh->do('CREATE FUNCTION comma_float EXTERNAL');

sub comma_float {
  my ($self, $sth, $n) = @_;
  $n =~ tr/,/./;
  return $n + 0.0;
}

my $sth = $dbh->prepare(
  'SELECT col3 FROM foo WHERE comma_float(col3) > 80.50 ORDER BY col3 ASC'
);
$sth->execute;

while (my $res = $sth->fetchrow_hashref) {
  say $res->{col3};
}
Run Code Online (Sandbox Code Playgroud)

产量

80,70
81,90
Run Code Online (Sandbox Code Playgroud)