4

我正在尝试使用带有 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};
}

现在,这看起来相当不错。问题是 SQL(意思是 SQL::Statement,它位于 DBI 和 DBD::CSV 后面的某个位置)没有将 中的数据col3视为中间带逗号的浮点值漂浮。相反,它将列视为整数,因为它不理解逗号。

以下是一些示例数据:

foo;foo;81,90
bar;bar;80,50
baz;baz;80,70

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

问题:我怎样才能告诉它把带逗号的数字当作浮点数?

我想过的事情:

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

将源 CSV 文件更改为使用点而不是逗号不是一种选择。

我愿意接受各种建议。也欢迎通过 SQL 处理整个 CSV 的其他方法。非常感谢。

4

1 回答 1

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

输出

80,70
81,90
于 2012-12-06T12:38:57.023 回答