18、DB2应用开发之PHP与PDO_IBM/PDO_ODBC实践指南

DB2应用开发之PHP与PDO_IBM/PDO_ODBC实践指南

1. DB2应用开发基础操作

在DB2应用开发中,执行SQL语句时,我们可以使用 db2_execute 函数。以下是一个示例代码,展示了如何执行语句并处理可能出现的错误:

$result = db2_execute($stmt); 
if (!$result) { 
    echo 'The execute failed. '; 
    echo 'SQLSTATE value: ' . db2_stmt_error(); 
    echo 'with Message: ' . db2_stmt_errormsg(); 
}

当操作完成后,需要释放相关资源,以避免资源浪费。可以使用以下函数来释放不同类型的资源:
- db2_free_stmt :释放语句资源。
- db2_free_result :释放结果集资源。
- db2_close :释放由 db2_connect 分配的连接资源。不过,如果使用 db2_pconnect 建立了持久连接, db2_close 请求将被忽略,连接资源会被下一个类似的连接使用。以下是使用 db2_close 的示例:

$result = db2_close($conn_resource); 
if ($result) { 
    echo 'Connection was successfully closed.'; 
}
2. PHP与PDO_IBM/PDO_ODBC开发

在使用PDO_IBM或PDO_ODBC进行PHP开发时,安装和配置DB2、相关驱动以及PHP和Apache后,需要在 php.ini 文件中编辑一些变量,以使PDO与DB2正常工作。具体如下:
- pdo_odbc.db2_instance_name :在Linux和UNIX系统中,此条目是必需的,它可以引导PHP找到DB2库所在的实例。假设实例名为 db2inst1 ,不同驱动的配置示例如下:
- pdo_ibm

[PDO_IBM instance name] 
pdo_ibm.db2_instance_name= db2inst1
  • PDO_ODBC
[PDO_ODBC instance name] 
pdo_odbc.db2_instance_name= db2inst1
  • pdo_odbc.connection_pooling :连接池通过重用之前建立的旧连接来提高数据库应用程序的性能,尤其适用于短时间连接的情况。可以配置 pdo_odbc.connection_pooling 的值来决定连接池的级别,不同级别如下表所示:
    | 连接池级别 | 描述 |
    | — | — |
    | Strict | 当连接凭证与现有关闭连接的连接选项完全匹配时,重用该连接。启用连接池时,建议使用此选项。 |
    | Relaxed | 重用具有匹配连接字符串的连接,并且不检查所有连接属性。 |
    | Off | 不使用连接池。 |
    当连接池设置为 Relaxed 时, php.ini 中的配置示例如下:
[PDO connection pooling] 
pdo_ibm.connection_pooling = relaxed

或者

[PDO connection pooling] 
pdo_odbc.connection_pooling = relaxed
  • pdo.dsn. *:数据源名称(DSN)包含通过ODBC或CLI驱动程序连接到数据库的相关信息,包括数据库名称、数据库驱动、用户名、密码等。可以在 php.ini 中为DSN字符串创建别名。以下是连接到 SAMPLE 数据库时,不同驱动的DSN别名配置示例:
  • pdo_ibm
[DSN alias for pdo_db2] 
pdo.dsn.dealerbase="ibm:sample"
  • pdo_odbc
[DSN alias for pdo_db2] 
pdo.dsn.dealerbase=”odbc:sample”

配置完成后,可以使用别名来连接数据库,示例代码如下:

$dbh = new PDO("dealerbase");
3. PDO程序流程

PDO编程采用面向对象的概念。连接到DB2数据库时,会创建一个PDO基类的实例,该实例作为数据库句柄,后续对数据库的操作都会使用到它。在操作数据库时会创建不同的对象,主要可分为以下几类:
- 连接对象(PDO的实例)
- 语句对象(PDOStatement的实例)
- 异常(PDOException的实例)

PDO程序在事务处理中的典型流程如下:

graph LR
    A[连接到数据库] --> B[准备并执行语句]
    B --> C[处理结果]
    C --> D[释放资源]
4. 连接到数据库

使用 new PDO() 调用PDO类的构造函数来建立与数据库的连接,构造函数有四组参数:
- 数据源名称(DSN)
- 用户名
- 密码
- 驱动选项

DSN包含连接数据库所需的信息,具体参数取决于连接的是编目数据库还是非编目数据库。连接DB2数据库时,建议使用编目连接。假设数据库名为 SAMPLE ,用户ID为 db2inst1 ,密码为 123 ,不同连接方式的DSN示例如下:
- 编目连接
- pdo_ibm ibm:DSN=sample;UID=db2inst1;PWD=123
- pdo_odbc odbc:DSN=sample;UID=db2inst1;PWD=123
- 非编目连接
- pdo_ibm ibm:DSN={IBM DB2 ODBC DRIVER};HOSTNAME=localhost;PORT=50000;DATABASE=sample;PROTOCOL=TCPIP;UID=db2inst1;PWD=123;
- pdo_odbc odbc:DSN={IBM DB2 ODBC DRIVER};HOSTNAME=localhost;PORT=50000;DATABASE=sample;PROTOCOL=TCPIP;UID=db2inst1;PWD=123;

以下是使用编目方法连接数据库的示例代码:

<?php 
try { 
    /* Use one of the following connection string */ 
    /* For PDO_IBM */ 
    $constrng = 'ibm:DSN=sample;UID=db2inst1;PWD=123'; 
    /* for PDO_ODBC */ 
    $constrng = 'odbc:DSN=sample;UID=db2inst1;PWD=123'; 
    $dbh = new PDO($constrng); 
    echo 'Connected'; 
} catch (PDOexception $exp) { 
    echo 'Exception: ' . $exp->getMessage(); 
} 
?>

DSN名称可以通过以下三种方式提供给PDO程序:
- 作为构造函数的参数 :将DSN作为字符串提供给程序,但这会使程序依赖于数据库。每次数据库位置或密码更改时,所有引用该数据库的程序都需要相应修改。示例代码如下:

<?php 
/* Use one of the following connection string */ 
/* The connection below is for pdo_ibm */ 
$constring = 'ibm:sample'; 
/* The connection below is for pdo_odbc */ 
$constring = 'odbc:sample'; 
try { 
    $dbh = new PDO($constrng, '', ''); 
    echo 'Connected'; 
} catch (PDOexception $exp) { 
    echo 'Exception: ' . $exp->getMessage(); 
} 
?>
  • 在文件中 :将DSN存储在文件中,程序引用该文件。当DSN参数值更改时,只需修改文件,无需修改程序。示例代码如下:
<?php 
$constrng = 'uri:file:///usr/local/dsnfile'; 
try { 
    $dbh = new PDO($constrng, '', ''); 
    echo 'Connected'; 
} catch (PDOexception $exp) { 
    echo 'Exception: ' . $exp->getMessage(); 
} 
?>
  • 在php.ini中使用别名 :与 ibm_db2 扩展类似,PDO_IBM/PDO_ODBC可以使用持久和非持久连接。如果通过设置 pdo_odbc.connection_pooling Strict Relaxed 来启用连接池,则不建议使用持久连接。以下是创建非持久连接的示例代码:
<?php 
/* Use one of the following connection strings */ 
/* The connection below is for pdo_ibm */ 
$constring = 'ibm:sample'; 
/* The connection below is for pdo_odbc */ 
$constring = 'odbc:sample'; 
try { 
    $dbh = new PDO($constrng, 'db2inst1', '123'); 
    echo 'Connected'; 
} catch (PDOexception $exp) { 
    echo 'Exception: ' . $exp->getMessage(); 
} 
?>

若要创建持久连接,可使用以下 new PDO 格式:

$dbh = new PDO($constrng, 'db2inst1', '123', array(PDO::ATTR_PERSISTENT=> true));
5. 准备和执行语句

在准备和执行SQL语句之前,需要确定事务的以下特性:
- 使用的游标类型
- 如何捕获错误
- 要使用的隔离级别

5.1 游标类型

PDO支持两种游标类型:
- Forward-only cursor(仅向前游标) :这是PDO驱动程序中的默认游标,也是最快的游标。在获取所有行之后且在发起另一个查询之前,务必使用 PDOStatement::closeCursor 方法关闭游标。可以在 PDOStatement::prepare 中使用 PDO::ATTR_CURSOR 并将其值设置为 PDO::CURSOR_FWDONLY 来设置仅向前游标。示例代码如下:

$sql = "SELECT c_id, c_name, c_email FROM customer WHERE c_name = :name"; 
try { 
    $sth = $dbh->prepare($sql, array(PDO::ATTR_CURSOR, PDO::CURSOR_FWDONLY)); 
    $sth->execute(array(':name' => 'Myname')); 
    print_r($sth->fetchAll()); 
} catch (PDOexception $exp) { 
    echo 'Exception: ' . $exp->getMessage(); 
}
  • Scrollable cursor(可滚动游标) :通过将 PDO::ATTR_CURSOR 设置为 PDO::CURSOR_SCROLL 来指定可滚动游标。示例代码如下:
$sql = "SELECT c_id, c_email FROM customer WHERE c_name = :name"; 
try { 
    $sth = $dbh->prepare($sql, array(PDO::ATTR_CURSOR, PDO::CURSOR_SCROLL)); 
    $sth->execute(array(':name' => 'Daniel')); 
    while ($row = $sth->fetch(PDO::FETCH_NUM, PDO::FETCH_ORI_NEXT)) { 
        $data = $row[0] . "\t" . $row[1] . "\t" . $row[2] . "\n"; 
        echo $data; 
    } 
    $sth->closeCursor(); 
} catch (PDOexception $exp) { 
    echo 'Exception: ' . $exp->getMessage(); 
}
5.2 错误捕获

PDO具有异常处理程序,用于捕获异常并显示其详细信息,使用类似于其他编程语言的“try and catch”结构。当PDO程序引发异常时,会创建一个 PDOException 实例。 PDOException 类是 Exception 类的扩展,有三种操作模式,通过设置 PDO::ATTR_ERRMODE 属性来强制使用不同的错误处理模式,具体如下表所示:
| 值 | 描述 |
| — | — |
| PDO::ERRMODE_SILENT | 这是PDO中的默认错误处理模式。发生错误时,相应的错误代码和消息会设置在语句对象(PDOStatement)和数据库对象(PDO)中。使用此模式的语句不会捕获错误,函数失败后会执行下一个语句。 |
| PDO::ERRMODE_WARNING | 此模式在开发或测试PDO应用程序时很有用。除了设置错误代码和消息外,还会引发一个 E_WARNING 。可以在函数名前使用“@”来抑制此警告。 |
| PDO::ERRMODE_EXCEPTION | 这是处理PDO程序中错误的最佳方式。一旦发生错误,会抛出一个异常,阻止 try 块中后续函数的执行。控制会转移到 catch 块中处理异常,并提供错误信息和堆栈跟踪。 |

以下是将错误处理模式设置为 PDO::ERRMODE_EXCEPTION 并使用 try and catch 块处理异常的示例代码:

<?php 
/* Use one of the following connection string */ 
/* The connection below is for pdo_ibm */ 
$constring = 'ibm:sample'; 
/* The connection below is for pdo_odbc */ 
$constring = 'odbc:sample'; 
$dbh = new PDO($constrng, 'db2inst1', '123'); 
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); 
$sql = "SELECT c_id FROM customer WHERE notexistingcol = :name "; 
$sth = $dbh->prepare($sql); 
try { 
    $sth->execute(array(':name' => 'ABC')); 
    $row = $sth->fetchAll(); 
    print_r($row); 
    $sth->closeCursor(); 
} catch (PDOexception $exp) { 
    echo 'Exception: ' . $exp->getMessage(); 
} 
?>

当异常发生时, getMessage() 方法会输出类似如下的信息:

Exception : SQLSTATE[42S22]: Column not found: -206 [IBM][CLI Driver][DB2/LINUX] SQL0206N "NOTEXISTINGCOL" is not valid in the context where it is used. SQLSTATE=42703 (SQLExecute[-206] at /root/Desktop/php-5.1.2/ext/pdo_odbc/odbc_stmt.c:133)

还可以使用 errorCode() errorInfo() 方法获取更多错误信息:
- PDO::errorCode() :提供错误的SQLSTATE信息。
- PDO::errorInfo() :提供错误的所有信息,如SQLSTATE、SQLCODE和数据库服务器的错误消息。

5.3 隔离级别

在PHP中使用PDO_IBM/PDO_ODBC时,选择隔离级别的考虑因素与之前描述的 ibm_db2 扩展相同。

5.4 准备和执行SQL语句

PDO提供了两种方式来准备和执行SQL语句:单步执行和分步执行。

单步执行 :通常用于在同一程序中不重复执行的SQL语句,可分为以下两种情况:
- 返回结果集的SQL语句 :使用 PDO::query 函数准备和执行返回结果集的SQL语句,结果集的列可以通过列位置和列名进行索引。示例代码如下:

<?php 
/* Use one of the following connection string */ 
/* The connection below is for pdo_ibm */ 
$constring = 'ibm:sample'; 
/* The connection below is for pdo_odbc */ 
$constring = 'odbc:sample'; 
$dbh = new PDO($constrng, 'db2inst1', '123'); 
$sql = 'SELECT c_id, c_name FROM customer'; 
try { 
    foreach ($dbh->query($sql) as $row) { 
        echo $row[0] . ' ' . $row['C_NAME']; 
    } 
} catch (PDOexception $exp) { 
    print_r($sth->errorInfo()); 
    echo 'Exception: ' . $exp->getMessage(); 
} 
?>
  • 不返回结果集的SQL语句 :使用 PDO::exec 函数准备和执行不返回结果集的SQL语句,该函数返回受影响的行数。例如, DELETE INSERT UPDATE 语句可以使用 PDO::exec 执行。示例代码如下:
<?php 
/* Use one of the following connection string */ 
/* The connection below is for pdo_ibm */ 
$constring = 'ibm:sample'; 
/* The connection below is for pdo_odbc */ 
$constring = 'odbc:sample'; 
$dbh = new PDO($constrng, 'db2inst1', '123'); 
$num = $dbh->exec("INSERT INTO customer (c_name, c_email) VALUES ('Piotr', 'aa123@123.com')"); 
echo 'Number of rows affected is: ' . $num; 
?>

分步执行 :如果计划多次执行相同的SQL语句,但使用不同的参数,建议采用分步执行的方式,这样不仅性能更好,而且更安全,因为每次绑定新参数时都会检查数据类型,避免SQL注入等情况。PDO中的参数标记有两种类型:
- 命名参数标记 :可以为参数标记命名,在名称前加上“:”,然后将该参数标记值绑定到SQL语句中以完成并执行。
- 无名参数标记 :在SQL语句中使用“?”符号,让DB2知道该参数标记的值将在后续绑定。

需要注意的是,不能在同一语句中同时使用命名和无名参数标记。准备和执行SQL语句的分步操作包括以下三个步骤:
1. 准备SQL语句 :使用 PDO::prepare 函数准备带或不带参数标记的SQL语句。可以在该函数中使用驱动选项选择要使用的游标,也可以稍后使用 setAttribute 方法进行设置。例如,在准备语句时将游标设置为可滚动:

$sth = $dbh->prepare($sql, array(PDO::ATTR_CURSOR, PDO::CURSOR_SCROLL));

PDO_IBM/PDO_ODBC的默认游标是仅向前游标。如果已将游标设置为可滚动,可以通过将 PDO::ATTR_CURSOR 属性设置为 PDO::CURSOR_FWDONLY 将游标改回仅向前游标。
2. 绑定参数 :有两种方式可以绑定SQL语句的参数:
- 使用绑定函数 :可以使用 PDOStatement::bindParam PDOStatement::bindValue 方法。这些方法可以用于通过 PDO::prepare 准备SQL语句后得到的 PDOstatement 实例。需要指定绑定数据的类型,具体类型如下:
- PDO::PARAM_INT :用于整数数据类型。
- PDO::PARAM_STR :用于字符串数据类型。
- PDO::PARAM_LOB :用于大对象。
- PDO::PARAM_NULL :用于绑定空值。

以下是使用命名参数标记结合 bindParam bindValues 的示例代码:

<?php 
/* Use one of the following connection string */ 
/* The connection below is for pdo_ibm */ 
$constring = 'ibm:sample'; 
/* The connection below is for pdo_odbc */ 
$constring = 'odbc:sample'; 
$dbh = new PDO($constrng, '', ''); 
$cid = 100; 
$name = 'ABC'; 
$sql = "SELECT c_id, c_name FROM customer WHERE c_id > :id AND c_name NOT LIKE :name"; 
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); 
try { 
    $sth = $dbh->prepare($sql); 
    $sth->bindValue(':id', $cid, PDO::PARAM_INT); 
    $sth->bindParam(':name', $name, PDO::PARAM_STR, 10); 
    $sth->execute(); 
    $result = $sth->fetchAll(); 
    print_r ($result); 
} catch (PDOexception $exp) { 
    print_r($sth->errorInfo()); 
    echo 'Exception: ' . $exp->getMessage(); 
} 
?>

使用 PDOStatement::bindParam 时,可以有一个额外的长度参数来强制绑定参数的长度。
- 通过数组传递参数值进行绑定 :以下是在PDO程序中使用无名参数标记的示例代码:

<?php 
/* Use one of the following connection string */ 
/* The connection below is for pdo_ibm */ 
$constring = 'ibm:sample'; 
/* The connection below is for pdo_odbc */ 
$constring = 'odbc:sample'; 
$dbh = new PDO($constrng, '', ''); 
$cid = 100; 
$name = 'ABC'; 
$sql = "SELECT c_id, c_name FROM customer WHERE c_id > ? AND c_name NOT LIKE ?"; 
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); 
try { 
    $sth = $dbh->prepare($sql); 
    $rc = $sth->bindValue(1, $cid, PDO::PARAM_INT); 
    $rc = $sth->bindParam(2, $name, PDO::PARAM_STR, 10); 
    $rc = $sth->execute(); 
    $result = $sth->fetchAll(); 
    print_r($result); 
} catch (PDOexception $exp) { 
    print_r($sth->errorInfo()); 
    echo 'Exception: ' . $exp->getMessage(); 
} 
?>
  1. 执行语句 :使用 PDOStatement::execute 函数执行准备好的语句。可以使用绑定函数绑定变量,也可以在执行时通过数组传递参数来绑定语句。以下是在 execute 函数中使用命名参数标记的示例代码:
<?php 
/* Use one of the following connection string */ 
/* The connection below is for pdo_ibm */ 
$constring = 'ibm:sample'; 
/* The connection below is for pdo_odbc */ 
$constring = 'odbc:sample'; 
$dbh = new PDO($constrng, '', ''); 
$id = 10; 
$sql = "select c_name from customer where c_id > :id"; 
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); 
try { 
    $sth = $dbh->prepare($sql); 
    $rc = $sth->execute(array(':id' => $id)); 
    $result = $sth->fetchAll(); 
    print_r($result); 
} catch (PDOexception $exp) { 
    print_r($sth->errorInfo()); 
    echo 'Exception: ' . $exp->getMessage(); 
} 
?>
5.5 创建和调用存储过程

使用PHP和PDO可以像执行其他SQL语句一样创建SQL存储过程。但如果需要从客户端调用存储过程,则需要根据参数类型进行绑定。存储过程中有三种类型的参数: IN OUT INOUT 。在绑定参数时,数据类型常量和参数使用按位或运算符(|)关联。 OUT INOUT 参数使用 PDO::PARAM_INPUT_OUTPUT 参数类型, IN 参数不需要指定参数类型。以下是一个包含所有三种参数类型并返回结果集的存储过程示例代码:

<?php 
/* Use one of the following connection string */ 
/* The connection below is for pdo_ibm */ 
$constring = 'ibm:sample'; 
/* The connection below is for pdo_odbc */ 
$constring = 'odbc:sample'; 
$dbh = new PDO($constrng, 'db2inst1', '123'); 
$id = 10; 
$sql = 'CALL getdetails(?, ?, ?)'; 
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); 
$amount = 0; 
$c_id = 140; 
$quantity = 100; 
try { 
    $sth = $dbh->prepare($sql); 
    // Input parameter 
    $sth->bindParam(1, $c_id, PDO::PARAM_INT); 
    // Output parameter 
    $sth->bindParam(2, $amount, PDO::PARAM_INT|PDO::PARAM_INPUT_OUTPUT); 
    // Input output parameter 
    $sth->bindParam(3, $quant, PDO::PARAM_INT|PDO::PARAM_INPUT_OUTPUT); 
    $sth->execute(); 
    echo 'The INOUT parameter is: ' . $quant . ' and the OUT parameter is: ' . $amount; 
    $rowset = $sth->fetchAll(PDO::FETCH_NUM); 
    print_r($rowset); 
} catch (PDOexception $exp) { 
    print_r($sth->errorInfo()); 
    echo 'Exception: ' . $exp->getMessage(); 
} 
?>

执行存储过程后,与存储过程参数标记绑定的变量将被填充存储过程执行的返回值。

5.6 处理事务

PDO默认以自动提交模式运行,即所有查询如果成功则提交,失败则回滚。可以使用 PDO::beginTransaction 方法将多个SQL查询作为一个事务的一部分,该函数会关闭PDO应用程序的自动提交模式。事务完成后,可以使用 PDO::commit 函数提交事务,或使用 PDO::rollback 函数回滚事务。以下是PHP使用DB2 PDO_IBM或PDO_ODBC处理事务的示例代码:

<?php 
/* Use one of the following connection string */ 
/* The connection below is for pdo_ibm */ 
$constring = 'ibm:sample'; 
/* The connection below is for pdo_odbc */ 
$constring = 'odbc:sample'; 
$dbh = new PDO($constrng, 'db2inst1', '123'); 
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); 
try { 
    $dbh->beginTransaction(); 
    $dbh->exec(" 
        INSERT INTO customer (c_name, c_email) VALUES ('ABC', 'a123@123.com') 
    "); 
    $dbh->exec(" 
        INSERT INTO customer (c_name, c_email) VALUES ('DEF', '1a23@123.com') 
    "); 
    $dbh->exec(" 
        INSERT INTO customer (c_name, c_email) VALUES ('GHI', '1n23@123.com') 
    "); 
    $dbh->commit(); 
} catch (PDOexception $exp) { 
    print 'Rolling back transaction'; 
    $dbh->rollback(); 
    print_r($dbh->errorInfo()); 
    echo 'Exception: ' . $exp->getMessage(); 
} 
?>

当发生错误时,由于错误处理模式设置为 PDO::ERRMODE_EXCEPTION ,会抛出异常,在 catch 块中回滚事务并打印错误信息。如果没有错误发生,则提交并完成事务。

6. 处理结果

PDO提供了两种获取数据的方式:
- Buffered fetch(缓冲获取) :将SQL查询返回的结果集中的所有行获取到一个数组中。如果查询返回的结果集非常大,不建议使用此方法,因为它会消耗大量系统资源。
- Non-buffered fetch(非缓冲获取) :使用 PDOstatement::fetch 方法逐行获取结果集并进行处理。 PDOStatement::fetch 有不同的选项来获取不同数据的列,具体如下:
- 使用FETCH_BOUND和bindColumn :此方法将结果集中返回的每一列分配给一个变量。执行 PDOStatement::fetch 后,绑定的变量将更新为结果集中对应的值。使用 PDO::FETCH_BOUND 作为 fetch 的参数,使绑定的列变量被 fetch 函数填充。示例代码如下:

<?php 
/* Use one of the following connection string */ 
/* The connection below is for pdo_ibm */ 
$constring = 'ibm:sample'; 
/* The connection below is for pdo_odbc */ 
$constring = 'odbc:sample'; 
$dbh = new PDO($constrng, '', ''); 
$id = 10; 
$sql = "select c_id, c_name from customer where c_id > :id"; 
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); 
try { 
    $sth = $dbh->prepare($sql); 
    $sth->execute(array(':id' => $id)); 
    $sth->bindColumn(1, $id); 
    $sth->bindColumn(2, $name); 
    while ($row = $sth->fetch(PDO::FETCH_BOUND)) { 
        echo 'id is: ' . $id . ' and name is: ' . $name; 
    } 
} catch (PDOexception $exp) { 
    print_r($sth->errorInfo()); 
    echo 'Exception: ' . $exp->getMessage(); 
} 
?>
  • 不传递参数调用Fetch函数 :这是默认选项, fetch 函数返回结果集中的每一行,列通过其位置进行索引。示例代码如下:
while ($row = $sth->fetch()) { 
    echo 'id is: ' . $row[0] . ' and name is: ' . $row[1]; 
}
  • 使用FETCH_ASSOC :将结果集中的行获取到一个以结果集列名索引的数组中。示例代码如下:
while ($row = $sth->fetch(PDO::FETCH_ASSOC)) { 
    echo 'id is: ' . $row['C_ID'] . ' and name is: ' . $row['C_NAME']; 
}
  • 使用FETCH_BOTH :将 PDO::FETCH_BOTH 作为参数传递给 PDOStatement::fetch ,返回的每一行数组同时以列名和列位置进行索引。示例代码如下:
while ( $row = $sth->fetch(PDO::FETCH_BOTH)) { 
    echo 'id is: ' . $row[0] . ' and name is: ' . $row['C_NAME']; 
}
  • 使用可滚动游标 :如果使用可滚动游标并传递 PDO::FETCH_ORI_NEXT 常量,可以从结果集中获取下一行。示例代码如下:
$sql = "SELECT * FROM customer"; 
try { 
    $offset = 0; 
    $sth = $dbh->prepare($sql, array(PDO::ATTR_CURSOR, PDO::CURSOR_SCROLL)); 
    $sth->execute(); 
    while ($row = $sth->fetch(PDO::FETCH_NUM, PDO::FETCH_ORI_NEXT)) { 
        $data = $row[0] . "\t" . $row[1] . "\t" . $row[2] . "\n"; 
        echo $data; 
    } 
    $sth->closeCursor(); 
} catch (PDOexception $exp) { 
    print_r($sth->errorInfo()); 
    echo 'Exception: ' . $exp->getMessage(); 
}
  • 将数据获取到对象中 :通过提供 PDO::FETCH_LAZY PDO::FETCH_OBJ 常量,可以将数据直接获取到对象中。在 PDO::FETCH_LAZY 模式下,用于访问列的对象变量名在访问时创建。可以通过列名或相应位置引用列。示例代码如下:
while ($row = $sth->fetch(PDO::FETCH_LAZY)) { 
    print_r($row->C_NAME); 
} 
while ($row = $sth->fetch(PDO::FETCH_OBJ)) { 
    print_r($row->C_NAME); 
}
7. 释放资源

每次获取数据后,需要关闭游标,以便重用语句资源,使用 PDOStatement::closeCursor 方法关闭游标。可以将连接资源赋值为 null 来释放连接资源。以下是一个示例代码:

// Connect to the database, use one of the following 
// for PDO_IBM 
$dbh = new PDO('ibm:sample', '', '');

通过以上步骤,你可以全面掌握使用PHP和PDO_IBM/PDO_ODBC进行DB2应用开发的方法,包括连接数据库、执行SQL语句、处理事务、获取结果和释放资源等操作。

DB2应用开发之PHP与PDO_IBM/PDO_ODBC实践指南

8. 综合示例:完整的DB2应用开发流程

为了更好地理解上述各个步骤在实际开发中的应用,下面给出一个综合示例,展示从连接数据库到处理结果并释放资源的完整流程。

<?php
// 步骤1: 连接到数据库
try {
    // 选择连接字符串,这里以pdo_ibm为例
    $constrng = 'ibm:DSN=sample;UID=db2inst1;PWD=123';
    $dbh = new PDO($constrng);
    echo 'Connected successfully.' . PHP_EOL;

    // 步骤2: 设置错误处理模式
    $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

    // 步骤3: 准备和执行SQL语句
    $sql = "SELECT c_id, c_name, c_email FROM customer WHERE c_id > :id";
    $sth = $dbh->prepare($sql);
    $id = 10;
    $sth->bindValue(':id', $id, PDO::PARAM_INT);
    $sth->execute();

    // 步骤4: 处理结果
    while ($row = $sth->fetch(PDO::FETCH_ASSOC)) {
        echo 'ID: ' . $row['c_id'] . ', Name: ' . $row['c_name'] . ', Email: ' . $row['c_email'] . PHP_EOL;
    }

    // 步骤5: 关闭游标
    $sth->closeCursor();

    // 步骤6: 释放连接资源
    $dbh = null;
    echo 'Connection closed.' . PHP_EOL;

} catch (PDOException $exp) {
    echo 'Exception: ' . $exp->getMessage() . PHP_EOL;
}
?>

这个示例展示了一个典型的DB2应用开发流程,包括连接数据库、设置错误处理模式、准备和执行SQL语句、处理结果、关闭游标以及释放连接资源。

9. 性能优化建议

在使用PHP和PDO_IBM/PDO_ODBC进行DB2应用开发时,为了提高应用的性能,可以考虑以下几点建议:
- 合理使用连接池 :如前文所述,连接池可以通过重用旧连接来提高性能。根据应用的实际情况,选择合适的连接池级别(Strict或Relaxed),并在 php.ini 中进行相应配置。
- 使用分步执行SQL语句 :当需要多次执行相同的SQL语句但参数不同时,采用分步执行的方式,即先准备语句,再绑定参数并执行。这样可以避免重复解析SQL语句,提高性能,同时增强安全性。
- 选择合适的游标类型 :根据实际需求选择合适的游标类型。如果只需要按顺序读取结果集,使用Forward-only cursor可以提高性能;如果需要随机访问结果集,则使用Scrollable cursor。
- 避免大结果集的缓冲获取 :如果查询可能返回非常大的结果集,避免使用Buffered fetch,因为它会消耗大量系统资源。建议使用Non-buffered fetch逐行处理结果集。

10. 常见错误及解决方法

在开发过程中,可能会遇到一些常见的错误,以下是一些常见错误及相应的解决方法:
| 错误类型 | 错误信息示例 | 可能原因 | 解决方法 |
| — | — | — | — |
| 连接错误 | Exception: SQLSTATE[08001]: [IBM][CLI Driver] SQL30081N A communication error has been detected. | 数据库服务器未启动、网络连接问题、连接信息错误 | 检查数据库服务器是否正常运行,检查网络连接是否稳定,检查连接字符串中的数据库名称、用户名、密码等信息是否正确 |
| SQL语法错误 | Exception: SQLSTATE[42601]: Syntax error: -104 [IBM][CLI Driver][DB2/LINUX] SQL0104N An unexpected token "INVALID_COLUMN" was found following "SELECT". | SQL语句存在语法错误 | 仔细检查SQL语句,确保语法正确。可以在数据库管理工具中测试该SQL语句 |
| 参数绑定错误 | Exception: SQLSTATE[HY093]: Invalid parameter number: parameter was not defined | 参数标记与绑定的参数不匹配 | 检查SQL语句中的参数标记和绑定的参数是否一致,确保参数名称和数量正确 |

11. 总结与展望

通过本文的介绍,我们详细了解了使用PHP和PDO_IBM/PDO_ODBC进行DB2应用开发的各个方面,包括基础操作、连接数据库、准备和执行SQL语句、处理事务、获取结果以及释放资源等。同时,我们还给出了性能优化建议和常见错误的解决方法。

在未来的DB2应用开发中,随着技术的不断发展,我们可以进一步探索更多的高级特性和优化技巧,例如使用存储过程和函数来实现复杂的业务逻辑,使用数据库索引来提高查询性能等。此外,结合其他技术和框架,如缓存技术、微服务架构等,可以构建更加高效、稳定和可扩展的DB2应用系统。

希望本文能够帮助开发者更好地掌握DB2应用开发的方法和技巧,在实际项目中取得更好的效果。

graph LR
    A[开始] --> B[连接数据库]
    B --> C[准备和执行SQL语句]
    C --> D{是否有结果集}
    D -- 是 --> E[处理结果]
    D -- 否 --> F[继续其他操作]
    E --> G[释放资源]
    F --> G
    G --> H[结束]

以上流程图展示了一个典型的DB2应用开发流程,从连接数据库开始,经过准备和执行SQL语句,根据是否有结果集进行相应处理,最后释放资源并结束。这个流程涵盖了本文介绍的主要内容,帮助开发者更好地理解和应用这些知识。

基于可靠性评估序贯蒙特卡洛模拟法的配电网可靠性评估研究(Matlab代码实现)内容概要:本文围绕“基于可靠性评估序贯蒙特卡洛模拟法的配电网可靠性评估研究”,介绍了利用Matlab代码实现配电网可靠性的仿真分析方法。重点采用序贯蒙特卡洛模拟法对配电网进行长时间段的状态抽样统计,通过模拟系统元件的故障修复过程,评估配电网的关键可靠性指标,如系统停电频率、停电持续时间、负荷点可靠性等。该方法能够有效处理复杂网络结构设备时序特性,提升评估精度,适用于含分布式电源、电动汽车等新型负荷接入的现代配电网。文中提供了完整的Matlab实现代码案例分析,便于复现和扩展应用。; 适合人群:具备电力系统基础知识和Matlab编程能力的高校研究生、科研人员及电力行业技术人员,尤其适合从事配电网规划、运行可靠性分析相关工作的人员; 使用场景及目标:①掌握序贯蒙特卡洛模拟法在电力系统可靠性评估中的基本原理实现流程;②学习如何通过Matlab构建配电网仿真模型并进行状态转移模拟;③应用于含新能源接入的复杂配电网可靠性定量评估优化设计; 阅读建议:建议结合文中提供的Matlab代码逐段调试运行,理解状态抽样、故障判断、修复逻辑及指标统计的具体实现方式,同时可扩展至不同网络结构或加入更多不确定性因素进行深化研究。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值