ZetCode

PHP PDOStatement::closeCursor 方法

最后修改于 2025 年 4 月 19 日

PDOStatement::closeCursor 方法释放与服务器的连接。这允许在当前语句仍有未获取的行时执行其他 SQL 语句。

基本定义

PDOStatement::closeCursor 清空 PDOStatement 对象的语句集。当您在完成当前语句集之前需要执行另一个查询时,这非常有用。

语法:public PDOStatement::closeCursor(): bool。该方法成功时返回 true,失败时返回 false。对于不支持多个活动语句的数据库来说,这一点尤为重要。

基本用法

这显示了 closeCursor 的最简单用法,即释放数据库资源。

close_cursor_basic.php
<?php

declare(strict_types=1);

try {
    $pdo = new PDO('mysql:host=localhost;dbname=testdb', 'user', 'password');
    $stmt = $pdo->query('SELECT * FROM large_table');
    
    // Process some rows
    for ($i = 0; $i < 10; $i++) {
        $row = $stmt->fetch();
        echo "Row {$i}: {$row['id']}\n";
    }
    
    // Free up the connection
    $stmt->closeCursor();
    
    // Execute another query
    $pdo->query('UPDATE stats SET processed = 1');
} catch (PDOException $e) {
    echo "Error: " . $e->getMessage();
}

此示例从一个大型语句集中获取 10 行,然后调用 closeCursor。这使得后续的 UPDATE 查询可以在不等待所有行被获取的情况下执行。

多个语句

演示了在同一连接上执行多个语句时使用 closeCursor。

close_cursor_multiple.php
<?php

declare(strict_types=1);

try {
    $pdo = new PDO('mysql:host=localhost;dbname=testdb', 'user', 'password');
    
    // First query
    $stmt1 = $pdo->query('SELECT * FROM users WHERE active = 1');
    while ($user = $stmt1->fetch()) {
        echo "Active user: {$user['name']}\n";
    }
    $stmt1->closeCursor();
    
    // Second query
    $stmt2 = $pdo->query('SELECT * FROM products WHERE stock < 10');
    while ($product = $stmt2->fetch()) {
        echo "Low stock: {$product['name']}\n";
    }
    $stmt2->closeCursor();
} catch (PDOException $e) {
    echo "Error: " . $e->getMessage();
}

这显示了两个查询之间的正确资源清理。在处理下一个语句之前,会先关闭每个语句。这可以防止 MySQL 中可能出现的“命令不同步”错误。

带有未获取结果

展示了在未获取所有结果的情况下如何使用 closeCursor。

close_cursor_unfetched.php
<?php

declare(strict_types=1);

try {
    $pdo = new PDO('mysql:host=localhost;dbname=testdb', 'user', 'password');
    $stmt = $pdo->query('SELECT * FROM large_dataset');
    
    // Only process first 100 rows
    $count = 0;
    while ($row = $stmt->fetch() && $count++ < 100) {
        processRow($row);
    }
    
    // Close cursor even though more rows exist
    $stmt->closeCursor();
    
    // Execute another query
    $pdo->exec('DELETE FROM temp_data');
} catch (PDOException $e) {
    echo "Error: " . $e->getMessage();
}

function processRow($row) {
    // Process row data
}

这里我们故意不获取语句集中的所有行。在执行另一个语句之前调用 closeCursor 来释放资源。这可以防止大型语句集出现内存泄漏。

在事务中

演示了在数据库事务中使用 closeCursor。

close_cursor_transaction.php
<?php

declare(strict_types=1);

try {
    $pdo = new PDO('mysql:host=localhost;dbname=testdb', 'user', 'password');
    $pdo->beginTransaction();
    
    // First operation
    $stmt1 = $pdo->query('SELECT * FROM accounts WHERE balance > 1000');
    $richAccounts = $stmt1->fetchAll();
    $stmt1->closeCursor();
    
    // Second operation
    $stmt2 = $pdo->prepare('UPDATE accounts SET flagged = 1 WHERE id = ?');
    foreach ($richAccounts as $account) {
        $stmt2->execute([$account['id']]);
    }
    
    $pdo->commit();
} catch (PDOException $e) {
    $pdo->rollBack();
    echo "Error: " . $e->getMessage();
}

此示例显示了事务中正确的游标管理。在处理结果和执行更新之前,会先关闭第一个查询的游标。这可以保持事务边界的清晰。

带有预准备语句

展示了在多次执行的预准备语句中使用 closeCursor 的用法。

close_cursor_prepared.php
<?php

declare(strict_types=1);

try {
    $pdo = new PDO('mysql:host=localhost;dbname=testdb', 'user', 'password');
    $stmt = $pdo->prepare('SELECT * FROM orders WHERE customer_id = ?');
    
    $customerIds = [5, 8, 12];
    foreach ($customerIds as $id) {
        $stmt->execute([$id]);
        $orders = $stmt->fetchAll();
        processOrders($orders);
        $stmt->closeCursor();
    }
} catch (PDOException $e) {
    echo "Error: " . $e->getMessage();
}

function processOrders($orders) {
    // Process customer orders
}

这演示了在重用预准备语句时正确的游标清理。每次执行和处理完结果后,都会调用 closeCursor 来重置语句以供下次执行。

错误处理

展示了如何处理使用 closeCursor 时的错误。

close_cursor_error.php
<?php

declare(strict_types=1);

try {
    $pdo = new PDO('mysql:host=localhost;dbname=testdb', 'user', 'password');
    $stmt = $pdo->query('SELECT * FROM products');
    
    // Process some rows
    while ($row = $stmt->fetch()) {
        echo "Product: {$row['name']}\n";
    }
    
    // Attempt to close cursor
    if (!$stmt->closeCursor()) {
        throw new Exception('Failed to close cursor');
    }
    
    // Execute another query
    $pdo->exec('UPDATE inventory SET checked = NOW()');
} catch (PDOException $e) {
    echo "Database error: " . $e->getMessage();
} catch (Exception $e) {
    echo "Error: " . $e->getMessage();
}

此示例检查 closeCursor 的返回值,如果失败则抛出异常。正确的错误处理可确保您了解任何游标清理问题。

带有存储过程

演示了调用返回多个结果集的存储过程时使用 closeCursor 的用法。

close_cursor_procedure.php
<?php

declare(strict_types=1);

try {
    $pdo = new PDO('mysql:host=localhost;dbname=testdb', 'user', 'password');
    $stmt = $pdo->query('CALL get_sales_report()');
    
    // Process first result set
    echo "Daily Sales:\n";
    while ($row = $stmt->fetch()) {
        echo "{$row['day']}: {$row['amount']}\n";
    }
    
    $stmt->closeCursor();
    
    // Move to next result set
    if ($stmt->nextRowset()) {
        echo "\nMonthly Sales:\n";
        while ($row = $stmt->fetch()) {
            echo "{$row['month']}: {$row['amount']}\n";
        }
    }
    
    $stmt->closeCursor();
} catch (PDOException $e) {
    echo "Error: " . $e->getMessage();
}

这显示了如何正确管理返回多个结果集的存储过程的游标。在处理每个结果集之间调用 closeCursor 以确保过渡的清晰。

最佳实践

来源

PHP PDOStatement::closeCursor 文档

本教程通过实际示例涵盖了 PDOStatement::closeCursor 方法,并展示了其在数据库操作中的重要性。

作者

我叫 Jan Bodnar,我是一名充满激情的程序员,拥有丰富的编程经验。自 2007 年以来,我一直撰写编程文章。至今,我已撰写了 1400 多篇文章和 8 本电子书。我在教学编程方面拥有超过十年的经验。

列出 所有 PHP PDO 函数