如何使用Perl中的DBI按顺序获取列名和行数据?

14

我正在使用DBI来查询SQLite3数据库。我的代码可以工作,但是它没有按照指定的顺序返回列。例如:

Query:  select col1, col2, col3, col4 from some_view;
Output:

    col3, col2, col1, col4
    3, 2, 1, 4
    3, 2, 1, 4
    3, 2, 1, 4
    3, 2, 1, 4
    ...

(values and columns are just for illustration)

我知道发生这种情况是因为我正在使用哈希表,但如果我只使用一个数组,我怎么才能获取列名呢?我想要的只是对于任何任意的查询都能得到如下类似的结果:

    col1, col2, col3, col4
    1, 2, 3, 4
    1, 2, 3, 4
    1, 2, 3, 4
    1, 2, 3, 4
    ...

(也就是说,我需要输出按正确顺序排列并带有列名。)
我是一个Perl新手,但我真的认为这应该是个简单的问题。(我以前在Ruby和PHP中做过这件事,但我在Perl文档中找不到我要找的东西。)
这是目前我所拥有的简化版本:
use Data::Dumper;
use DBI;

my $database_path = '~/path/to/db.sqlite3';

$database = DBI->connect(
  "dbi:SQLite:dbname=$database_path",
  "",
  "",
  {
    RaiseError => 1,
    AutoCommit => 0,
  }
) or die "Couldn't connect to database: " . DBI->errstr;

my $result = $database->prepare('select col1, col2, col3, col4 from some_view;')
    or die "Couldn't prepare query: " . $database->errstr;

$result->execute
    or die "Couldn't execute query: " . $result->errstr;

########################################################################################### 
# What goes here to print the fields that I requested in the query?
# It can be totally arbitrary or '*' -- "col1, col2, col3, col4" is just for illustration.
# I would expect it to be called something like $result->fields
########################################################################################### 

while (my $row = $result->fetchrow_hashref) {
    my $csv = join(',', values %$row);
    print "$csv\n";
}

$result->finish;

$database->disconnect;
6个回答

19

用以下代码替换“what goes here”注释和随后的循环:

my $fields = join(',', @{ $result->{NAME_lc} });
print "$fields\n";

while (my $row = $result->fetchrow_arrayref) {
    my $csv = join(',', @$row);
    print "$csv\n";
}

NAME_lc 返回字段名的小写形式。 您还可以使用NAME_uc获取大写形式,或者使用NAME获取数据库返回的任何形式。

您还应该使用Text::CSVText::CSV_XS而不是尝试编写自己的CSV文件,但这是另一个问题。


好的,我会尝试一下。我本来会使用Text::CSV,但目前只是为了测试输出而已。 - Benjamin Oakes
1
工作得很好。再次感谢。难怪当我搜索时它没有出现 - Google对@{ $result->{NAME} }这样的东西不太友好。 - Benjamin Oakes
1
好的,搜索的地方是http://search.cpan.org/perldoc?DBI(或者你本地的副本)。 - cjm
我之前遇到过这个问题,但是从来没有在那里找到解决方案。现在我知道该寻找什么,所以看到了解决方法。 - Benjamin Oakes

3

在SELECT之前用数组定义列名

理想情况下,你应该有一个使用DBI的列名列表,并使用该数组。

如果需要从哈希本身获取列名,则可以使用此方法,您可以对其进行排序,但哈希中没有原始SQL SELECT顺序的指示:

my %cols_hash = ("name" => "john", "age" => 2, "color" => "apalachian");
my $cols_hash_ref = \%cols;  

my @keys = (sort keys %$cols_hash_ref);  
my @vals;  
foreach (@keys){ push @vals, $$cols_hash_ref{$_} };  

希望这能帮到您。
当我搜索时,我发现了一种从DBI获取列名的方法:
$sth = $dbh->prepare($query) or die "Prepare exceptioin: $DBI::errstr!";  
$rv = $sth->execute() or die "Execute exception: $DBI::errstr";  
$res = $sth->fetchall_arrayref();  

# Array reference with cols captions, which were retrived.  
$col_names_array_ref = $sth->{NAME};          

这应该会按原始顺序给出列名,但我还没有测试过。


3
如果您想保留顺序,但仍然希望使用哈希表按名称引用字段,请使用以下代码:
$dbh->selectall_arrayref($sql,{ Slice => {} } );

这将为您提供一个有序的哈希数组。

2

Here's what I do:

    use Data::Dump qw(dump);
    # get column names in array
    my @column_names_array= $sth->{NAME};  
    # print out column names in pretty format
    print "Field names: \n";
    dump(@column_names_array);

1

你正在请求以哈希形式返回结果。哈希本质上是无序的。也许你想使用fetchrow_arrayref

实际上,如果你查看了keys %$row,你会发现相应的键也是无序的。这就是哈希的特性...每个键都与其值配对,但键或值的整体顺序是针对访问进行优化的,而不是外部排序。


我知道这是因为我正在使用哈希表,但如果我只使用数组,我怎么才能获取列名呢?这就是我遇到的问题。 - Benjamin Oakes
1
请参阅 DBI 文档中的“语句句柄属性”。 - Randal Schwartz
根据我在Perl中所读到的,'keys'和'values'函数作用于同一个哈希时,保证返回的相应数组中元素的顺序相同。因此,一个技巧是获取第一行,抓取字段名称,然后继续处理其余行。 - jerseyboy
@jerseyboy,只有在调用keysvalues之间哈希没有被修改的情况下才能起作用。无法保证在调用fetchrow_hashref时结果一致。 - cjm

0
#!/usr/bin/perl                                                                                                                                                                        
use strict;
use warnings;
use DBI;
my $database = "your_mysqldb";
my $username = "your_dbusername";
my $password = "your_dbpassword";
my $tblename = "your_mysqldbtable";
my $dsn = "DBI:mysql:embedded";
#                                                                                                                                                                                      
my %attr = ( PrintError=>0,   #turn off error reporting via warn()                                                                                                                     
             RaiseError=>1);  #turn on error reporting via die()                                                                                                                       
my $dbh = DBI->connect($dsn,$username,$password,\%attr);
print "Connected to the MySQL database $database for username $username\n";
my $sql = "DESCRIBE ".$tblename;
my $sth = $dbh->prepare($sql);  
$sth->execute();   #execute the prepared query                                                                                                                                         
my @heads;
my $i=0;
while(my $r = $sth->fetchrow_arrayref()){
    $heads[$i]=$r->[0];
    $i++;
}
my $j=0;
foreach(@heads){
    print "$j $_\n";
    $j++;
}
$sth->finish();
$dbh->disconnect(); #disconnect from mysql db   

你的回答可以通过提供更多支持信息来改进。请[编辑]以添加更多细节,例如引用或文档,以便他人确认您的答案是正确的。您可以在帮助中心中找到有关如何编写良好答案的更多信息。 - Community

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接