perl DBI 使用详解
2014-05-15 17:09:00 阿炯

本站赞助商链接,请多关照。 使用DBI的方法:
---------------------------------------------------------------------
use DBI;
---------------------------------------------------------------------
注意:特定的DBD 级的模块不需要包括use行,因为在与服务器进行连接时,DBI负责激活相应的模块,比如DBD::mysql。

一、一些变量表达的常用意思
$drh表示驱动程序对象的句柄
$dbh表示针对一个数据库对象的句柄
$sth表示针对一个语句或者一个查询对象的句柄
$fh表示一个打开文件的句柄
$h表示一个通用的句柄,其含意有赖于上下文
$rc表示操作代码返回的布什值(真或假)
$rv表示操作代码返回的整数值
$rows表示操作代码返回的行数值
$str表示操作代码返回的字符串
@ary表示查询返回的一行值的数组(列表)
@row_ary表示从查询中返回的一个行的数组

二、关于DBI的一些驱动对象方法
查询子类函数:DBI->available_drivers
---------------------------------------------------------------
foreach(DBI->available_drivers){print;}
---------------------------------------------------------------------
结果:DBI::mysql
数据源函数:DBI->data_sources
---------------------------------------------------------------
foreach(DBI->data_sources){print;}
---------------------------------------------------------------------
结果:对于大多数正确的驱动子类来说,返回值一般为空,对于不能连接、用户名或密码为空的驱动子类,返回值不为空
数据库方法调用:DBI->install_driver
---------------------------------------------------------------------
my $drh=DBI->install_driver("mysql");
---------------------------------------------------------------------
结果:提供驱动子类的句柄,可以使用$drh->func("createdb", $db_name, $db_host, $username, $password, "admin");来创建数据库
数据库连接:DBI->connect
---------------------------------------------------------------

my $dsn="dbi:mysql:database=${db_name};hostname=${db_host};mysql_socket=${db_sock};

port=${db_port};mysql_compression=1;mysql_read_default_file=$ENV{HOME}/.my.cnf;mysql_read_default_group=client";

my %attr=( PrintError=>0, RaiseError=>0);
my $dbh=DBI->connect($dsn, $user_name, $password, [,\%attr]);
---------------------------------------------------------------------
结果:返回值是针对一个数据库的句柄,提供数据库连接功能,关闭连接使用$dbh->disconnect,其中很多参数可以省略,包括数据库名称。

DBI 接口 API

下面为最为常见与使用的接口函数,可以满足在 Perl 程序中使用 SQLite 数据库的基本需求;若需要了解更多细节,请查看 Perl DBI 官方文档。

序号API & 描述
1DBI->connect($data_source, "", "", \%attr)

建立一个到被请求的 $data_source 的数据库连接或者 session。如果连接成功,则返回一个数据库处理对象。

数据源形式如下所示:DBI:SQLite:dbname='test.db'。其中,SQLite 是 SQLite 驱动程序名称,test.db 是 SQLite 数据库文件的名称。如果文件名 filename 赋值为 ':memory:',那么它将会在 RAM 中创建一个内存数据库,这只会在 session 的有效时间内持续。

如果文件名 filename 为实际的设备文件名称,那么它将使用这个参数值尝试打开数据库文件;如果该名称的文件不存在,那么将创建一个新的命名为该名称的数据库文件。

可以保留第二个和第三个参数为空白字符串,最后一个参数用于传递各种属性。

2$dbh->do($sql)

该例程准备并执行一个简单的 SQL 语句。返回受影响的行数,如果发生错误则返回 undef。返回值 -1 意味着行数未知,或不适用 ,或不可用。在这里,$dbh 是由 DBI->connect() 调用返回的处理。

3$dbh->prepare($sql)

该例程为数据库引擎后续执行准备一个语句,并返回一个语句处理对象。

4$sth->execute()

该例程执行任何执行预准备的语句需要的处理。如果发生错误则返回 undef。如果成功执行,则无论受影响的行数是多少,总是返回 true。在这里,$sth 是由 $dbh->prepare($sql) 调用返回的语句处理。

5$sth->fetchrow_array()

该例程获取下一行数据,并以包含各字段值的列表形式返回。在该列表中,Null 字段将作为 undef 值返回。

6$DBI::err

这相当于 $h->err。其中,$h 是任何的处理类型,比如 $dbh、$sth 或 $drh。该程序返回最后调用的驱动程序(driver)方法的数据库引擎错误代码。

7$DBI::errstr

这相当于 $h->errstr。其中,$h 是任何的处理类型,比如 $dbh、$sth 或 $drh。该程序返回最后调用的 DBI 方法的数据库引擎错误消息。

8$dbh->disconnect()

该例程关闭之前调用 DBI->connect() 打开的数据库连接。



三、数据库句柄方法的调用
1.do方法:
my $rows=$dbh->do($query[, \%attr[, @bind_values]]);
语法解释:准备并运行$query表示的查询。返回值为受影响的行数—如果不知道行数,则返回-1,如果出现错误,则返回undef。
如果受影响的行数为0,则返回值为字符串“0E0”,作为数值它与0等价,但在判断时它为真。
一般用于非SELECT查询,不检索行的语句,如DELETE、INSERT、REPLACE或UPDATE,主要使用do()。
如果对SELECT语句使用它,则不会获得返回的语句句柄,也不能提取任何行。
当语句包括占位符(在此查询字符串内部由‘?’字母表示)时,使用@bind_values。
@bind_values为给占位符赋值的值的列表。它必须和占位符有一样多的值。如果只指定赋值
的值,但没有指定属性,则将undef作为\%attr参数的值来传递。
与prepare和execute结合使用的功能基本相同。

2.ping方法:
my $rc=$dbh->ping();
语法解释:检查与服务器的连接是否仍然有效,并相应返回真或假。

3.prepare方法:
my $sth=$dbh->prepare($query[, \%attr]);
语法解释:为以后的执行所准备的由$query表示的查询,并返回一个语句句柄。返回的句柄可用于execute(),以便执行该查询。

4.quote方法:
my $str=$dbh->quote($value[, $data_type]);
语法解释:处理字符串以实现SQL语句中特定字符的引用和转义,以便在执行这条语句时,该字符串不引起语法错误。
例如,“‘I \’m happy’”(没有双引号)返回字符串“I ’m happy”。
如果$value为undef,则它返回字符串“NULL”(没有引号)。
一般来说,$data_type参数不是必需的,因为MySQL将查询中指定为字符串的值自动地转换为其他类型。
可以将$data_type指定为特殊类型值的提示————例如,DBI::SQL_INTEGER指出$value表示一个整数。
不要使用具有打算利用占位符插入到查询中的值的quote()。DBI会自动引用这样的值。

5.selectall_arrayref方法
my $ary_ref=$dbh->selectall_arrayref($query[, \%attr[, @bind_values]]);
语法解释:执行由$query指定的查询,并结合prepare()、execute()和fetchall_arrayref()返回结果。如果出现错误,则返回undef。
如果$query参数是以前准备的语句,则省略prepare()步骤。
@bind_values参数和do()方法中的该参数具有同样的意义。
---------------------------------------------------------------------
# fetch all rows into a reference to an array of references
my $matrix_ref=$dbh->selectall_arrayref($query);
# determine dimensions of matrix
my $rows=(!defined($matrix_ref) ? 0 : scalar(@{$matrix_ref}));
my $cols=($rows == 0 ? 0 : scalar(@{$matrix_ref->[0]}));
for(my $i=0; $i < $rows; $i ++) # print each row
{
my $delim="";
for(my $j=0; $j < $cols; $j ++){
$matrix_ref->[$i][$j]="" if !defined($matrix_ref->[$i][$j]); # NULL?
print $delim . $matrix_ref ->[$i][$j];
$delim=",";
}
print "\n";
}
---------------------------------------------------------------------

6.selectcol_arrayref方法
my @ary_ref=$dbh->selectcol_arrayref($query[, \%attr[, @bind_values]]);
语法解释:执行由$query指定的查询,并通过组合prepare()和execute()返回结果的第一列。返回结果作为对含有每行第一列的数组的引用。如果出现错误,则返回undef。
如果$query参数是以前准备的语句,则省略prepare()步骤。
@bind_values参数和do()方法中的该参数具有同样的意义。

7.selectrow_array方法
my @row_ary=$dbh->selectrow_array($query[, \%attr[, @bind_values]]);
语法解释:执行由$query指定的查询,并结合prepare()、execute()和fetchall_arrayref()返回结果的第一行。
如果参数$query是以前准备的语句,则省略prepare()步骤。
如果在列表的上下文中调用时,selectrow_array()返回代表行值的数组,或者,如果出现错误,则返回空数组。在标量的上下文 中,selectrow_array()返回这个数组的第一个元素的值(行的第一列)。如果出现错误,则返回undef。
@bind_values参数和do()方法中的相应参数具有同样的意义。

四、语句句柄方法的调用
1.bind_col方法
my $rc=$sth->bind_col($col_num, \$var_to_bind);
将SELECT查询的给定列与Perl变量相联系,将它作为引用传递。$col_num的范围为1到查询选择的列数。每次提取行时,这个变量用列值自动更新。
bind_col()应该在execute()之前及prepare()之后调用。
如果列号范围不在1到查询选择的列数之间,则bind_col()返回假。

2.bind_columns方法
my $rc=$sth->bind_columns(\$var_to_bind1, \$var_to_bind2, ...);
将一系列变量与由准备好的SELECT 语句返回的列相联系,请参阅bind_col()方法的说明。
如果引用的数量与查询选择的列数不匹配,则bind_columns()返回假。

3.bind_param方法
my $rv=$sth->bind_param($n, $value[, \%attr]);
my $rv=$sth->bind_param($n, $value[, $bind_type]);
在一个语句中,将值与占位符‘?’相联系。应该在execute()之前及prepare()之后调用它。
$n指定了占位符的数量,应该限定$value值,而且该值范围应该为1到占位符的数量。为了限定NULL值,可传递undef。
参数\%attr或者$bind_type可作为要联系的值的类型提示。

4.dump_results方法
my $rows=$sth->dump_results([$maxlen[, $line_sep[, $field_sep[, $fh]]]]);
从语句句柄$sth 中提取所有的行,通过调用实用函数DBI::neat_list()将他们格式化,并将他们打印到给定的文件句柄中。返回提取的行数
$maxlen、$line_sep、$field_sep和$fh的缺省值分别为35、“\n”、“,”和STDOUT。

5.execute方法:
my $rows=$sth->execute([@bind_values]);
执行准备好的语句。如果该语句执行成功,则返回真,如果发生错误,则返回undef。
参数@bind_values与do()方法中的有相同的意义。

6.etchall_arrayref方法:
my $tbl_ary_ref=$sth->fetchall_arrayref([$slice_array_ref]);
从语句句柄$sth 中提取所有行,并返回数组的引用,这个数组包含提取的每行的一个引用。数组中每个引用的意义取决于所传递的参数。没有参数或者只有数组部分引用参数, 则$tbl_ary_ref 的每个元素都是包括结果集的一行值的数组引用。对于散列部分的引用参数,$tbl_ary_ref 的每个元素就是对包含结果集的一行值的散列引用。

7.fetchrow_array方法:
my @ary_ref=$sth->fetchrow_array();
当在一个列表的范围中调用时,fetchrow_array()返回包含结果集下一行列值的数组,如果不再有行或者发生错误,则 fetchrow_array()返回一个空数组。在标量上下文中,fetchrow_array()返回数组第一个元素的值(那就是说,行的第一列),如果不再有行或者发生错误,则fetchrow_array()返回undef。
通过检查$sth->err(),可以将结果集正常结束与出现错误区分开来。零值表明已经无错误地到达了结果集的末尾,一般与while使用。
---------------------------------------------------------------------
while(my @ary=$sth->fetchrow_array()){
@ary=map { defined($_) ? $_ : "NULL" } @ary;
print join(",", @ary) . "\n";
}
---------------------------------------------------------------------

8.fetch或fetchrow_arrayref方法
my @ary_ref=$sth->fetchrow_arrayref();
返回一个包括结果集的下一行列值的数组引用。如果不再有行或者发生错误,则返回undef。
通过检查$sth->err(),可以将结果集正常结束与出现错误区分开来。零值表明已经无错误地到达了结果集的末尾,一般与while使用。
-----------------------------------------------------------------------------------
my @matrix=(); # array of array references
while(my @ary=$sth->fetchrow_array()) { # fetch each row
push(@matrix, [ @ary ]); # save reference to just-fetched row
}
# determine dimensions of matrix
my $rows=scalar(@matrix);
my $cols=($rows == 0 ? 0 : scalar(@{$matrix[0]}));
for(my $i=0; $i < $rows; $i++) { # print each row
my $delim="";
for(my $j=0; $j < $cols; $j++) {
$matrix[$i][$j]="" if !defined($matrix[$i][$j]); # NULL value?
print $delim . $matrix[$i][$j];
$delim=",";
}
print "\n";
}
---------------------------------------------------------------------

9.fetchrow_hashref方法
my $hash_ref=$sth->fetchrow_hashref([$name]);
返回包括结果集的下一行列值的散列引用。如果不再有行或者发生错误,则返回undef。散列是索引值是列名称,散列的元素是列值。
对于散列的关键值,指定变量$name说明使用的语句句柄属性。缺省值为“NAME”。这可能导致查询中的列名称不区分大小写的问题,但是散列键是区分大 小写的。要强迫散列键为大写字母或者小写字母,可以指定“NAME_lc”或“NAME_uc”的$name值。
通过检查$sth->err(),可以将结果集正常结束与出现错误区分开来。零值表明已经无错误地到达了结果集的末尾,一般与while使用。

---------------------------------------------------------------------
while(my $hash_ref=$sth->fetchrow_hashref()) {
my $delim="";
foreach my $key(keys(%{$hash_ref})) {
$hash_ref->{$key}="" if !defined($hash_ref->{$key}); # NULL value?
print $delim . $hash_ref->{$key};
$delim=",";
}
print "\n";
}
---------------------------------------------------------------------

10.finish方法
my $rc=$sth->finish();
释放有关语句句柄的任何资源。通常不必显式地调用这个方法,但是如果只提取部分结果集,则调用finish()使DBI了解已经提取了数据。调用 finish()可能使语句属性无效,最好在调用execute()之后立即访问它们。

11.rows方法
my $rv=$sth->rows();
返回与$sth相关的语句所作用的行数,如果发生错误,则返回-1。使用这个方法主要用于不返回行的语句。对于SELECT语句,不能依赖rows()方法在提取行时统计行数。

五、通用句柄方法
下面的方法不是专用于特定类型的句柄的。可用驱动程序、数据库或语句句柄来调用它们。
1.$h->err()
返回最近调用的驱动程序操作的数字错误代码。0表示没有错误。

2.$h->errstr()
返回最近调用的驱动程序操作的字符串错误消息。空字符串表示没有错误。

3.DBI->trace($trace_level[, $trace_filename]);
$h->trace($trace_level[, $trace_filename]);
设置跟踪级别。跟踪提供有关DBI操作的信息。跟踪级别的范围从0(关闭)到9(最多信息)。通过作为DBI类方法或独立的句柄调用跟踪,跟踪可以启用脚本内部的所有DBI操作:
DBI->trace(2);打开脚本跟踪
$sth->trace(2);打开句柄跟踪
通过设置DBI_TRACE环境变量,也可以对运行的所有DBI脚本在全局级别启用跟踪。缺省时,跟踪输出到STDERR.。提供的$filename参 数可以直接将结果输出到不同的文件。将输出添加到这个文件的任何已有内容后面。
每个跟踪调用导致来自所有跟踪的句柄中的输出进入相同的文件。如果文件已命名,则所有跟踪就输出到那个文件。如果没有命名的文件,则所有跟踪输出到STDERR。

4.DBI->trace_msg($str[, $min_level])
$h->trace_msg($str[, $min_level])
如果跟踪这个句柄或如果在DBI级启用跟踪,则编写这个跟踪输出的消息。如果启用DBI级的跟踪,则trace_msg()可以作为 DBI->trace_msg()来调用,编写消息。只有在跟踪级别至少为这个级别时,才可以提供$min_level 参数来指定应该编写的消息。

六、MySQL 的特定管理方法
DBI 作为直接访问驱动程序的手段所供的func() 函数方法
---------------------------------------------------------------------
$rc=$drh->func("createdb", $dbname, [host, user, password,], 'admin');
$rc=$drh->func("dropdb", $dbname, [host, user, password,], 'admin');
$rc=$drh->func("shutdown", [host, user, password,], 'admin');
$rc=$drh->func("reload", [host, user, password,], 'admin');
---------------------------------------------------------------------
or
---------------------------------------------------------------------
$rc=$dbh->func("createdb", $dbname, 'admin');
$rc=$dbh->func("dropdb", $dbname, 'admin');
$rc=$dbh->func("shutdown", 'admin');
$rc=$dbh->func("reload", 'admin');
---------------------------------------------------------------------
通过驱 动程序句柄或通过数据库句柄访问func()方法。驱动程序句柄与打开的连接无关,所以,如果以这种方式访问func(),则必须提供允许这个方法创建连 接的主机名称、用户名称和口令的参数。如果用数据库句柄访问func(),则不需要那些参数。如果需要,可以像下面这样获得驱动程序句柄:
my $drh=DBI->install_driver(“mysql”);#(“mysql”mustbelowercase)
createdb创建由$db_name指定的数据库。要这样做,必须对该数据库拥有CREAT权限。
dropdb删除由$db_name指定的数据库。要这样做,必须对该数据库拥有DROP权限。当心,如果删除了一个数据库,则它将会消失,且再也不能恢复。
shutdown关闭服务器。必须具有SHUTDOWN权限。
reload告诉服务器重新加载授权表。如果直接使用DELETE、INSERT或UPDATE而不是使用GRANT或REVOKE来修改这个授权表的内容,则这是必需的。要使用reload,必须具有RELOAD权限。

七、DBI 环境变量
DBI考虑了几个环境变量,如表G-3所示。除了DBI_TRACE之外,所有变量都由connect()方法使用。DBI_TRACE由trace()方法使用。
DBI_DRIVER:DBI级的驱动程序名(MySQL的“mysql”)
DBI_DSN:数据源名
DBI_PASS:口令
DBI_TRACE:跟踪级别和/或跟踪输出文件
DBI_USER:用户名称
---------------------------------------------------------------------
$errno=$dbh->{'mysql_errno'};
$error=$dbh->{'mysql_error};
$info=$dbh->{'mysql_hostinfo'};
$info=$dbh->{'mysql_info'};
$insertid=$dbh->{'mysql_insertid'};
$info=$dbh->{'mysql_protoinfo'};
$info=$dbh->{'mysql_serverinfo'};
$info=$dbh->{'mysql_stat'};
$threadId=$dbh->{'mysql_thread_id'};
---------------------------------------------------------------------

八、PerlDBI快速回顾与实用

基本流程
1.1 连接数据库
1.2 执行SQL语句
    1.2.1 技巧:对SQL进行排版
    1.2.2 通过SQL语句中的参数优化查询执行效率
1.3 读取记录
    1.3.1 fetchrow_array
    1.3.2 fetchrow_arrayref
    1.3.3 fetchrow_hashref
1.4 结束一个SQL会话
1.5 断开数据库连接
参考资源

下文以常见的MySQL为例,说说如何实现对数据库的操作。

基本流程

习惯在Windows下开发数据库、熟悉ADO、ADO.NET的开发者一定对ADOConnection/ADODataSet/ADOTable等类耳熟能详。DBI的接口与之类似,但在操作方法上又有不同,对ADO熟悉的朋友不妨比较一下异同。一般来说,数据库操作由以下几个步骤组成一个常见的流程:
1.建立一个数据库连接
2.通过建立的数据库连接,执行SQL语句
3.执行SQL后获取返回的数据集
4.在数据集中对记录进行处理,一般是一个循环的过程
5.处理完毕,关闭数据库连接,释放资源

下面是按照上述的流程,在Perl中访问MySQL的一段代码,以这段代码为例说明DBI的使用方法。
#!/usr/bin/perl -w
use strict;
use DBI;

my $dbh = DBI->connect("DBI:mysql:test:192.168.1.2", 'root', 'password');
my $sth = $dbh->prepare("SELECT * FROM test1");
$sth->execute();

while ( my @row = $sth->fetchrow_array() ){
   print join('\t', @row)."\n";
}

$sth->finish();
$dbh->disconnect();

注意代码中的灰色部分就是要特别关注的数据库访问接口,这里展现的只是一部分,下面将会依次说明每一个步骤,以及其它的操作在Perl中是如何实现的。

1.1 连接数据库

my $dbh = DBI->connect("DBI:mysql:test:192.168.1.2", 'root', 'password');

调用DBI的方法DBI->connect来建立一个数据库的连接,如果连接成功则返回一个数据库连接句柄,之后执行SQL等操作都要把这个连接句柄作为一个操作参数。在connect调用中,首先要提供一个数据库连接串。这个连接串用冒号分为了几个部分,请看下表

小节    说明
DBI    接口类型
mysql    数据库类型
test    数据库名称
192.168.1.2    数据库主机地址

在前面例子中的连接串中,DBI表示这是DBI接口的一个连接串;mysql表示要连接的数据库是MySQL数据库(如果要连接Oracle数据库,这里则是oracle),不同的数据库有不同的连接串定义,可以参考DBI对应的访问驱动的说明;test指明了连接到数据库主机上的数据库名称;192.168.1.2就是MySQL服务器的IP地址。这里要注意的是,连接串中的数据库类型mysql必须小写。如果省略了主机名,则缺省为localhost。connect方法的后面两个参数是连接数据库主机的用户名和密码,这个可是不可缺少的。

如果在连接过程中出现任何错误,则connect的返回值都会是undef(和C语言中的NULL是一回事)。这里为了简化而略去了错误检查,实际做项目的时候应当对这些错误和返回值的进行检查。

1.2 执行SQL语句

my $sth = $dbh->prepare("SELECT * FROM test1");
$sth->execute();
$dbh->do(“UPDATE test1 SET time=now()”);

连接上了数据库,获得了数据库连接句柄,就可以利用这个句柄来对数据库进行操作了。要执行一条SQL语句,为了提高性能,DBI分两个步骤来做。先把SQL语句通过prepare方法提交到数据库,数据库为该语句分配执行资源,之后调用execute方法通知数据库执行该SQL语句。注意prepare方法是通过数据库连接句柄调用的,如果成功则返回一个该SQL的句柄,之后通过该SQL语句句柄调用execute执行SQL。

一般来说execute执行的都是返回数据的语句(例如SELECT语句)。反之如果执行INSERT、UPDATE、DELETE、CREATE
 TABLE等不需要返回数据的语句,则有一个更方便、快速的方法 $dbh->do(SQL语句),可以省去prepare的步骤。do方法返回的是受该SQL影响的记录数量。
 
1.2.1 技巧:对SQL进行排版
常写大段SQL的朋友可能会对于SQL中的引号很头痛,每每因为引号的问题搞的SQL语句乱成一团分辨不清。由于qq中的字符串同双引号内的字符串一样会对变量进行解释,同时qq还可以换行。因此使用它来对SQL进行排版是非常好的一个选择,例如像这样的一条SQL语句:
my $res_operator = $dbhandle->prepare( qq{
   SELECT o_customerid, COUNT(*) AS totalMsgNum FROM mm4fcdrs
   WHERE (m_date>'$begindate') AND (m_date<'enddate')
   GROUP BY o_customerid
});

根本无需考虑引号的问题,可以和正常情况一样的写SQL,是不是方便了很多?

1.2.2 通过SQL语句中的参数优化查询执行效率
在执行大量INSERT之类的语句的时候,反复向数据库服务器提交同样结构的一个SQL语句,在这种情况下可以利用prepare和SQL参数来优化执行效率:
1.先使用prepare提交一个SQL模板给数据库服务器,把其中值的部分用参数占位符代替。
2.使用prepare让服务器为该SQL准备了执行资源后,调用execute并在该方法中传入参数实际的值执行SQL。
3.之后可以反复调用execute,不需要服务器重新prepare。

假设要执行这样的一个系列的SQL

INSERT INTO test1 VALUES (NULL, ‘a’, ‘2005-04-01’)
... ...
INSERT INTO test1 VALUES (NULL, ‘z’, ‘2005-04-01’)

其中第二个字段的值是从a到z的字母。那么可以这样来优化执行效率:
my $sth = $dbh->prepare( qq{INSERT INTO test1 VALUES (NULL, ?, ‘2005-04-01’)} );
for my $value('a'..'z')  {
    $sth->execute($value);
}

其中的问号就是前面说的参数占位符了,它的意思就是告诉在准备执行资源的服务器:这个SQL的这个位置会有一个值,但是现在还不知道,等下执行的时候再告诉你。prepare了之后,用一个循环产生a-z的字符给变量\$value,然后将\$value在execute方法中作为一个参数传入,服务器那里会自动用传入的值替换前面的\"?\"。需要提醒的是,传入的参数个数一定要和SQL中的占位符的数量一样。

1.3 读取记录

熟悉ADO的朋友一定知道里面有一个DataReader对象,DBI中读取数据的方法和它非常的相似。简单来说,就是单向、流式的读取数据,也就是每次只能向后读一条数据直到没有数据可以读取。如开头的例子中,用了 $sth->fetchrow_array() 方法来读取数据。其实DBI读取数据还有几种常见的方法,这几个方法是类似的,所不同的是返回记录的形式。

1.3.1 fetchrow_array
返回一个由字段的值组成的数组。该数组的第1个元素就是当前记录第1个字段的值。

while ( my @row = $sth->fetchrow_array() )  {
    print "$row[0], $row[1], $row[2]\n";
}

或者这样,不过要注意字段对应的顺序

while ( my ($id, $name, $time) = $sth->fetchrow_array() )  {
    print "$id, $name, $time\n";
}

1.3.2 fetchrow_arrayref
返回由字段的值组成的数组的引用。同fetchrow_array的区别很明显,fetchrow_arrayref返回的数组的引用。

while ( my $row_ref = $sth->fetchrow_arrayref() ) {
for (my $i = 0; $i < @{$row_ref}; $i++) {
    print "$row_ref->[$i]\t";
}
print "\n";
}

这里要注意的是,如果要取字段的个数,需要把这个引用转成数组的形式获得@{$row_ref} 。获取数组元素的值的时候,因为\$row_ref是引用,因此需要使用->操作符。

1.3.3 fetchrow_hashref
返回一个由”字段名-字段值”这样的”键-值”对组成的HASH表。关键的不同就是,只有这个方法可以通过一个字段名获得它的值,而不必关心这个字段是第几个字段。而前者只能依靠索引来访问值。不过缺点就是,效率要比前面两个差一些。

while ( my $record = $sth->fetchrow_hashref() ) {
for my $field( keys %{$record} ) {
    print "$field: $record->{$field}\t";
}
print "\n";
}

这里需要复习一下HASH表的操作方法。keys操作符获取HASH的键(key)的数组,$record->{$field}获得HASH表中\$field对应的值。注意这里同样是引用,因此要用->操作符。

使用上面三个方法可以基本解决问题了。此外还有两个方法fetchall_arrayref和selectall_arrayref可以直接通过SQL一次性获取整个数据集,不过使用上稍微复杂一些,要涉及到perl的scalar 操作符,这里就不赘述了。有兴趣的读者可以参考DBI的相关资料。

最后是收尾工作。

1.4 结束一个SQL会话

$sth->finish();

1.5 断开数据库连接

$dbh->disconnect();

很简单明了,就不赘述了。

Perl中利用DBI访问数据库的接口基本上就是这些了,还有一些高级的内容留给有兴趣的读者自己发掘研究了。可能有些读者会感觉没有ADO、ADO.NET操作起来方便,但是在脚本的环境下能够如此方便的操作数据库,比起用C接口来说已经方便很多了。也许在看完这片文章之后的不久,可以在cpan上发现你的Module和全世界的Perl程序员一起分享呢。

九、一个Web例子
---------------------------------------------------------------------
use CGI::Carp "fatalsToBrowser";
use strict;
use warnings;
use DBI;
use CGI qw(:standard escapeHTML escape);
 
my($driver_name, $db_name, $db_host, $db_sock, $db_port, $db_user, $db_pswd, $dsn);
$driver_name='mysql';
$db_name='mydata';
$db_host='localhost';
$db_sock='/tmp/mysql.sock';
$db_port='3306';
$db_user='cnangel';
$db_pswd='cnangel';
$dsn="dbi:mysql:database=${db_name};hostname=${db_host};mysql_socket=${db_sock};port=${db_port}";
 
# ... set up connection to database(not shown) ...
my $dbh=DBI->connect($dsn, $db_user, $db_pswd,
{ RaiseError=>1, PrintError=>0 });
 
# put out initial part of page
my $title="$db_name Database Browser";
print header();
print start_html(-title=>$title, -bgcolor=>"white");
print h1($title);
 
# parameters to look for in URL
my $tbl_name=param("tbl_name");
my $sort_col=param("sort_col");
 
# If $tbl_name has no value, display a clickable list of tables.
# Otherwise, display contents of the given table. $sort_col, if
# set, indicates which column to sort by.

!defined($tbl_name) ? display_table_names($dbh, $db_name) : display_table_contents($dbh, $tbl_name, $sort_col);

print end_html();

sub display_table_names {
my($dbh, $db_name)=@_;
print p("Select a table by clicking on its name:");
 
# retrieve reference to single-column array of table names
my $ary_ref=$dbh->selectcol_arrayref(qq{ SHOW TABLES FROM $db_name });
 
# Construct a bullet list using the ul()(unordered list) and
# li()(list item) functions. Each item is a hyperlink that
# re-invokes the script to display a particular table.
my @item;
foreach my $tbl_name(@{$ary_ref}){
my $url=sprintf("%s?tbl_name=%s", url(), escape($tbl_name));
my $link=a({-href=>$url}, escapeHTML($tbl_name));
push(@item, li($link));
}
print ul(@item);
}
 
sub display_table_contents{
my($dbh, $tbl_name, $sort_col)=@_;
my @rows;
my @cells;
# if sort column not specified, use first column
$sort_col="1" if !defined($sort_col);
 
# present a link that returns user to table list page
print p(a({-href=>url()}, "Show Table List"));
 
print p(strong("Contents of $tbl_name table:"));
 
my $sth=$dbh->prepare(qq{
SELECT * FROM $tbl_name ORDER BY $sort_col
LIMIT 200
});
$sth->execute();
 
# Use the names of the columns in the database table as the
# headings in an HTML table. Make each name a hyperlink that
# causes the script to be reinvoked to redisplay the table,
# sorted by the named column.
 
foreach my $col_name(@{$sth->{NAME}}){
my $url=sprintf("%s?tbl_name=%s;sort_col=%s",
url(),
escape($tbl_name),
escape($col_name));
my $link=a({-href=>$url}, escapeHTML($col_name));
push(@cells, th($link));
}
push(@rows, Tr(@cells));
 
# display table rows
while(my @ary=$sth->fetchrow_array()){
@cells=();
foreach my $val(@ary){
# display value if non-empty, else display non-breaking space
if(defined($val) && $val ne "") {
$val=escapeHTML($val);
}
else{
$val=" ";
}
push(@cells, td($val));
}
push(@rows, Tr(@cells));
}

# display table with a border
print table({-border=>"1"}, @rows);
}

1, AutoCommit — 是否自动提交
一般来说自动提交不利于应用程序的性能.
$dbh->{AutoCommit}=1;
print “AutoCommit: $dbh->{AutoCommit}\n”;

2, ChopBlanks — 取舍CHAR类型后面的空格
当你从数据库中取CHAR类型的值时, 你可以指定要不要带后面的空格, 在数据库中CHAR是定长的, 长足不足时, 后面以空格补充, 但你在写应用程序时, 可能并不想要后面的空格, 这时你可以将这个选项设为1.
$dbh->{ChopBlanks}=1;
print “ChopBlanks: $dbh->{ChopBlanks}\n”;

3, LongTruncOK — 是否允许载断返回值
如果返回值(如Long或LOB类型)的值大于缓冲区的长度, 有两个选项, 一种是报错, 另一种是只取缓冲区的长度的值, 丢弃后面的值. 设为True, 则不报错而只取一部份值, 设为False则报错.
$dbh->{LongTruncOk}=1;
print “LongTruncOk: $dbh->{LongTruncOk}\n”;

4, LongReadLen — 指定Long或LOB缓冲的最长大度
在数据库中, Long或LOB类型可以存放2GB或更多的值, 在Perl应用程序去取这些值时, 必须要指定一个最大的缓冲区大小, 和LongTruncOk配合, 可以取出一定长度的值.
$dbh->{LongReadLen}=1048576;
print “LongReadLen: $dbh->{LongReadLen}\n”;

更详细的信息可以下载perl DBI的中文帮助文档

参考资源

DBI官方网站
O'Reily《Programming the Perl DBI》