Perl - 数据库访问

本章教你如何在 Perl 脚本中访问数据库。 从 Perl 5 开始,使用 DBI 模块编写数据库应用程序变得非常容易。 DBI 代表 Perl 的 Database Independent Interface,这意味着 DBI 在 Perl 代码和底层数据库之间提供了一个抽象层,让您可以非常轻松地切换数据库实现。

DBI 是 Perl 编程语言的数据库访问模块。 它提供了一组方法、变量和约定,这些方法、变量和约定提供了一致的数据库接口,独立于所使用的实际数据库。


DBI 应用程序的架构

DBI 独立于后端可用的任何数据库。 无论您使用的是 Oracle、MySQL 还是 Informix 等,都可以使用 DBI。从下面的架构图中可以清楚地看到这一点。

Perl 数据库模块 DBI 架构

这里 DBI 负责通过 API(即应用程序编程接口)获取所有 SQL 命令,并将它们分派给适当的驱动程序以供实际执行。 最后,DBI 负责从驱动程序获取结果并将其返回给调用脚本。


符号和约定

本章将使用以下符号,建议您也应遵循相同的约定。

$dsn    Database source name
$dbh    Database handle object
$sth    Statement handle object
$h      Any of the handle types above ($dbh, $sth, or $drh)
$rc     General Return Code  (boolean: true=ok, false=error)
$rv     General Return Value (typically an integer)
@ary    List of values returned from the database.
$rows   Number of rows processed (if available, else -1)
$fh     A filehandle
undef   NULL values are represented by undefined values in Perl
\%attr  Reference to a hash of attribute values passed to methods

数据库连接

假设我们要使用 MySQL 数据库。 在连接到数据库之前,请确保以下内容。 如果您不了解如何在 MySQL 数据库中创建数据库和表,您可以参考我们的 MySQL 教程。

  • 您已经创建了一个名为 TESTDB 的数据库。

  • 您已经在 TESTDB 中创建了一个名为 TEST_TABLE 的表。

  • 此表包含字段 FIRST_NAME、LAST_NAME、AGE、SEX 和 INCOME。

  • 用户 ID"testuser"和密码"test123"设置为访问 TESTDB。

  • Perl Module DBI 已正确安装在您的机器上。

  • 您已通过 MySQL 教程了解 MySQL 基础知识。

以下是连接 MySQL 数据库"TESTDB"的示例 −

#!/usr/bin/perl

use DBI
use strict;

my $driver = "mysql"; 
my $database = "TESTDB";
my $dsn = "DBI:$driver:database=$database";
my $userid = "testuser";
my $password = "test123";

my $dbh = DBI->connect($dsn, $userid, $password ) or die $DBI::errstr;

如果与数据源建立连接,则返回数据库句柄并将其保存到 $dbh 以供进一步使用,否则 $dbh 设置为 undef 值并且 $DBI::errstr 返回错误字符串。


INSERT 操作

当您想在表中创建一些记录时,需要进行 INSERT 操作。 这里我们使用表 TEST_TABLE 来创建我们的记录。 因此,一旦我们的数据库连接建立,我们就可以在 TEST_TABLE 中创建记录了。 以下是在 TEST_TABLE 中创建单个记录的过程。 您可以使用相同的概念创建任意数量的记录。

记录创建需要以下步骤 −

  • 使用 INSERT 语句准备 SQL 语句。 这将使用 prepare() API 完成。

  • 执行 SQL 查询以从数据库中选择所有结果。 这将使用 execute() API 完成。

  • 释放声明句柄。 这将使用 finish() API 完成。

  • 如果一切顺利,则commit提交此操作,否则您可以rollback回滚完成事务。 下一节将解释提交和回滚。

my $sth = $dbh->prepare("INSERT INTO TEST_TABLE
                       (FIRST_NAME, LAST_NAME, SEX, AGE, INCOME )
                         values
                       ('john', 'poul', 'M', 30, 13000)");
$sth->execute() or die $DBI::errstr;
$sth->finish();
$dbh->commit or die $DBI::errstr;

使用绑定值

可能存在未预先给出要输入的值的情况。 因此,您可以使用绑定变量,该变量将在运行时获取所需的值。 Perl DBI 模块使用问号代替实际值,然后在运行时通过 execute() API 传递实际值。 以下是示例 −

my $first_name = "john";
my $last_name = "poul";
my $sex = "M";
my $income = 13000;
my $age = 30;
my $sth = $dbh->prepare("INSERT INTO TEST_TABLE
                        (FIRST_NAME, LAST_NAME, SEX, AGE, INCOME )
                          values
                        (?,?,?,?)");
$sth->execute($first_name,$last_name,$sex, $age, $income) 
          or die $DBI::errstr;
$sth->finish();
$dbh->commit or die $DBI::errstr;

读操作

对任何数据库的读取操作意味着从数据库中获取一些有用的信息,即从一个或多个表中获取一个或多个记录。 所以一旦我们的数据库连接建立起来,我们就可以对这个数据库进行查询了。 以下是查询所有 AGE 大于 20 的记录的过程。这将需要四个步骤 −

  • 根据所需条件准备 SQL SELECT 查询。 这将使用 prepare() API 完成。

  • 执行 SQL 查询以从数据库中选择所有结果。 这将使用 execute() API 完成。

  • 逐一获取所有结果并打印这些结果。这将使用 fetchrow_array() API 完成。

  • 释放声明句柄。 这将使用 finish() API 完成。

my $sth = $dbh->prepare("SELECT FIRST_NAME, LAST_NAME
                        FROM TEST_TABLE 
                        WHERE AGE > 20");
$sth->execute() or die $DBI::errstr;
print "Number of rows found :" + $sth->rows;
while (my @row = $sth->fetchrow_array()) {
   my ($first_name, $last_name ) = @row;
   print "First Name = $first_name, Last Name = $last_name\n";
}
$sth->finish();

使用绑定值

可能存在未预先给出条件的情况。 因此,您可以使用绑定变量,它将在运行时获取所需的值。 Perl DBI 模块使用问号代替实际值,然后在运行时通过 execute() API 传递实际值。 以下是示例 −

$age = 20;
my $sth = $dbh->prepare("SELECT FIRST_NAME, LAST_NAME
                        FROM TEST_TABLE
                        WHERE AGE > ?");
$sth->execute( $age ) or die $DBI::errstr;
print "Number of rows found :" + $sth->rows;
while (my @row = $sth->fetchrow_array()) {
   my ($first_name, $last_name ) = @row;
   print "First Name = $first_name, Last Name = $last_name\n";
}
$sth->finish();

UPDATE 操作

UPDATE 对任何数据库的操作意味着更新数据库表中已有的一条或多条记录。 以下是更新所有 SEX 为"M"的记录的过程。 在这里,我们将所有男性的 AGE 增加一年。 这将需要三个步骤 −

  • 根据所需条件准备 SQL 查询。 这将使用 prepare() API 完成。

  • 执行 SQL 查询以从数据库中选择所有结果。 这将使用 execute() API 完成。

  • 释放声明句柄。 这将使用 finish() API 完成。

  • 如果一切顺利,则commit提交此操作,否则您可以rollback回滚完成事务。 有关提交和回滚 API,请参阅下一节。

my $sth = $dbh->prepare("UPDATE TEST_TABLE
                        SET   AGE = AGE + 1 
                        WHERE SEX = 'M'");
$sth->execute() or die $DBI::errstr;
print "Number of rows updated :" + $sth->rows;
$sth->finish();
$dbh->commit or die $DBI::errstr;

使用绑定值

可能存在未预先给出条件的情况。 因此,您可以使用绑定变量,它将在运行时获取所需的值。 Perl DBI 模块使用问号代替实际值,然后在运行时通过 execute() API 传递实际值。 以下是示例 −

$sex = 'M';
my $sth = $dbh->prepare("UPDATE TEST_TABLE
                        SET   AGE = AGE + 1
                        WHERE SEX = ?");
$sth->execute('$sex') or die $DBI::errstr;
print "Number of rows updated :" + $sth->rows;
$sth->finish();
$dbh->commit or die $DBI::errstr;

在某些情况下,您想设置一个未预先给出的值,因此您可以按如下方式使用绑定值。 在此示例中,所有男性的收入将设置为 10000。

$sex = 'M';
$income = 10000;
my $sth = $dbh->prepare("UPDATE TEST_TABLE
                        SET   INCOME = ?
                        WHERE SEX = ?");
$sth->execute( $income, '$sex') or die $DBI::errstr;
print "Number of rows updated :" + $sth->rows;
$sth->finish();

DELETE 操作

当您想从数据库中删除一些记录时,需要进行 DELETE 操作。 以下是从 TEST_TABLE 中删除 AGE 等于 30 的所有记录的过程。此操作将执行以下步骤。

  • 根据所需条件准备 SQL 查询。 这将使用 prepare() API 完成。

  • 执行 SQL 查询以从数据库中删除所需的记录。 这将使用 execute() API 完成。

  • 释放声明句柄。 这将使用 finish() API 完成。

  • 如果一切顺利,则commit提交此操作,否则您可以rollback回滚完成事务。

$age = 30;
my $sth = $dbh->prepare("DELETE FROM TEST_TABLE
                         WHERE AGE = ?");
$sth->execute( $age ) or die $DBI::errstr;
print "Number of rows deleted :" + $sth->rows;
$sth->finish();
$dbh->commit or die $DBI::errstr;

使用 do 语句

如果您正在执行 UPDATE、INSERT 或 DELETE,则没有从数据库返回的数据,因此有一个捷径可以执行此操作。 您可以使用 do 语句来执行任何命令,如下所示。

$dbh->do('DELETE FROM TEST_TABLE WHERE age =30');

do 如果成功则返回真值,如果失败则返回假值。 实际上,如果成功,它会返回受影响的行数。 在示例中,它将返回实际删除的行数。


提交操作

COMMIT 是向数据库发出绿色信号以完成更改的操作,在此操作之后,任何更改都不能恢复到其原始位置。

这是一个调用 commit API 的简单示例。

$dbh->commit or die $dbh->errstr;

回滚操作

如果您对所有更改不满意,或者在任何操作之间遇到错误,您可以恢复这些更改以使用 rollback API。

这是一个调用rollback API 的简单示例。

$dbh->rollback or die $dbh->errstr;

开始事务

许多数据库都支持事务。 这意味着您可以进行一大堆会修改数据库的查询,但实际上并没有进行任何更改。 最后,您发出特殊的 SQL 查询 COMMIT,所有更改都同时进行。 或者,您可以发出查询 ROLLBACK,在这种情况下,所有更改都将被丢弃,数据库保持不变。

Perl DBI 模块提供了 begin_work API,它启用事务(通过关闭 AutoCommit)直到下一次调用提交或回滚。 下次提交或回滚后,AutoCommit 将自动再次开启。

$rc  = $dbh->begin_work  or die $dbh->errstr;

自动提交选项

如果您的事务很简单,您可以省去必须发出大量提交的麻烦。 当您进行连接调用时,您可以指定一个 AutoCommit 选项,该选项将在每次成功查询后执行自动提交操作。 这是它的样子 −

my $dbh = DBI->connect($dsn, $userid, $password,
              {AutoCommit => 1}) 
              or die $DBI::errstr;

这里 AutoCommit 可以取值 1 或 0,其中 1 表示 AutoCommit 开启,0 表示 AutoCommit 关闭。


自动错误处理

当您进行连接调用时,您可以指定一个自动为您处理错误的 RaiseErrors 选项。 发生错误时,DBI 将中止您的程序,而不是返回失败代码。 如果您只想在出现错误时中止程序,这会很方便。 这是它的样子 −

my $dbh = DBI->connect($dsn, $userid, $password,
              {RaiseError => 1})
              or die $DBI::errstr;

这里 RaiseError 可以取值 1 或 0。


断开数据库

要断开数据库连接,请使用 disconnect API,如下所示 −

$rc = $dbh->disconnect  or warn $dbh->errstr;

遗憾的是,disconnect 方法的事务行为是未定义的。 一些数据库系统(例如 Oracle 和 Ingres)会自动提交任何未完成的更改,但其他数据库系统(例如 Informix)会回滚任何未完成的更改。 不使用 AutoCommit 的应用程序应在调用断开连接之前显式调用提交或回滚。


使用 NULL 值

未定义值或 undef 用于指示 NULL 值。 您可以插入和更新具有 NULL 值的列,就像使用非 NULL 值一样。 这些示例使用 NULL 值插入和更新列年龄 −

$sth = $dbh->prepare(qq {
         INSERT INTO TEST_TABLE (FIRST_NAME, AGE) VALUES (?, ?)
       });
$sth->execute("Joe", undef);

这里 qq{} 用于将带引号的字符串返回给 prepare API。 但是,在 WHERE 子句中尝试使用 NULL 值时必须小心。 考虑 −

SELECT FIRST_NAME FROM TEST_TABLE WHERE age = ?

将 undef (NULL) 绑定到占位符不会选择具有 NULL 年龄的行! 至少对于符合 SQL 标准的数据库引擎来说。 有关原因,请参阅您的数据库引擎的 SQL 手册或任何 SQL 书籍。 要明确选择 NULL,您必须说"WHERE age IS NULL"。

一个常见问题是让代码片段处理在运行时可以定义或 undef(非 NULL 或 NULL)的值。 一种简单的技术是根据需要准备适当的语句,并将占位符替换为非 NULL 情况 −

$sql_clause = defined $age? "age = ?" : "age IS NULL";
$sth = $dbh->prepare(qq {
         SELECT FIRST_NAME FROM TEST_TABLE WHERE $sql_clause
       });
$sth->execute(defined $age ? $age : ());

其他一些 DBI 函数

available_drivers

@ary = DBI->available_drivers;
@ary = DBI->available_drivers($quiet);

通过在@INC 中的目录搜索 DBD::* 模块,返回所有可用驱动程序的列表。 默认情况下,如果某些驱动程序在早期目录中被其他同名驱动程序隐藏,则会发出警告。 为 $quiet 传递一个真值将禁止警告。

installed_drivers

%drivers = DBI->installed_drivers();

返回"已安装"(加载)到当前进程中的所有驱动程序的驱动程序名称和驱动程序句柄对列表。 驱动程序名称不包含"DBD::"前缀。

data_sources

@ary = DBI->data_sources($driver);

返回通过指定驱动程序可用的数据源(数据库)列表。 如果 $driver 为空或 undef,则使用 DBI_DRIVER 环境变量的值。

quote

$sql = $dbh->quote($value);
$sql = $dbh->quote($value, $data_type);

通过转义字符串中包含的任何特殊字符(例如引号)并添加所需类型的外引号,引用字符串文字以用作 SQL 语句中的文字值。

$sql = sprintf "SELECT foo FROM bar WHERE baz = %s",
                $dbh->quote("Don't");

对于大多数数据库类型,quote 将返回 'Don''t'(包括外引号)。 quote() 方法返回一个计算结果为所需字符串的 SQL 表达式是有效的。 例如 −

$quoted = $dbh->quote("one\ntwo\0three")

may produce results which will be equivalent to

CONCAT('one', CHAR(12), 'two', CHAR(0), 'three')

所有句柄通用的方法

err

$rv = $h->err;
or
$rv = $DBI::err
or
$rv = $h->err

从最后调用的驱动程序方法返回本机数据库引擎错误代码。 代码通常是整数,但您不应该假设。 这相当于 $DBI::err or $h->err。

errstr

$str = $h->errstr;
or
$str = $DBI::errstr
or
$str = $h->errstr

从最后调用的 DBI 方法返回本机数据库引擎错误消息。 这与上述"err"方法具有相同的寿命问题。 这相当于 $DBI::errstr 或 $h->errstr。

rows

$rv = $h->rows;
or
$rv = $DBI::rows

这将返回受先前 SQL 语句影响的行数,相当于 $DBI::rows。

trace

$h->trace($trace_settings);

DBI 具有非常有用的能力,可以生成关于它正在做什么的运行时跟踪信息,这在尝试跟踪 DBI 程序中的奇怪问题时可以节省大量时间。 您可以使用不同的值来设置跟踪级别。 这些值从 0 到 4 不等。值 0 表示禁用跟踪,4 表示生成完整的跟踪。


禁止插值语句

强烈建议不要使用如下插值语句 −

while ($first_name = <>) {
   my $sth = $dbh->prepare("SELECT * 
                          FROM TEST_TABLE 
                          WHERE FIRST_NAME = '$first_name'");
   $sth->execute();
   # and so on ...
}

因此不要使用插值语句,而是使用 绑定值 来准备动态 SQL 语句。