PHP Conference Japan 2024

存储过程

MySQL 数据库支持存储过程。存储过程是存储在数据库目录中的子程序。应用程序可以调用并执行存储过程。 CALL SQL 语句用于执行存储过程。

参数

存储过程可以具有 ININOUTOUT 参数,具体取决于 MySQL 版本。mysqli 接口没有针对不同类型的参数的特殊概念。

IN 参数

输入参数由 CALL 语句提供。请确保值已正确转义。

示例 #1 调用存储过程

<?php

mysqli_report
(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = new mysqli("example.com", "user", "password", "database");

$mysqli->query("DROP TABLE IF EXISTS test");
$mysqli->query("CREATE TABLE test(id INT)");

$mysqli->query("DROP PROCEDURE IF EXISTS p");
$mysqli->query("CREATE PROCEDURE p(IN id_val INT) BEGIN INSERT INTO test(id) VALUES(id_val); END;");

$mysqli->query("CALL p(1)");

$result = $mysqli->query("SELECT id FROM test");

var_dump($result->fetch_assoc());

以上示例将输出

array(1) {
  ["id"]=>
  string(1) "1"
}

INOUT/OUT 参数

使用会话变量访问 INOUT/OUT 参数的值。

示例 #2 使用会话变量

<?php

mysqli_report
(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = new mysqli("example.com", "user", "password", "database");

$mysqli->query("DROP PROCEDURE IF EXISTS p");
$mysqli->query('CREATE PROCEDURE p(OUT msg VARCHAR(50)) BEGIN SELECT "Hi!" INTO msg; END;');

$mysqli->query("SET @msg = ''");
$mysqli->query("CALL p(@msg)");

$result = $mysqli->query("SELECT @msg as _p_out");

$row = $result->fetch_assoc();
echo
$row['_p_out'];

以上示例将输出

Hi!

应用程序和框架开发人员可能能够使用会话变量和数据库目录检查的混合来提供更方便的 API。但是,请注意基于目录检查的自定义解决方案可能对性能产生的影响。

处理结果集

存储过程可以返回结果集。使用 mysqli::query() 无法正确获取从存储过程返回的结果集。 mysqli::query() 函数将语句执行和获取第一个结果集组合到一个缓冲结果集中(如果有)。但是,还有一些隐藏的用户无法访问的存储过程结果集,导致 mysqli::query() 无法返回用户期望的结果集。

使用 mysqli::real_query()mysqli::multi_query() 获取从存储过程返回的结果集。这两个函数都允许获取语句(如 CALL)返回的任意数量的结果集。如果未获取存储过程返回的所有结果集,则会导致错误。

示例 #3 从存储过程获取结果

<?php

mysqli_report
(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = new mysqli("example.com", "user", "password", "database");

$mysqli->query("DROP TABLE IF EXISTS test");
$mysqli->query("CREATE TABLE test(id INT)");
$mysqli->query("INSERT INTO test(id) VALUES (1), (2), (3)");

$mysqli->query("DROP PROCEDURE IF EXISTS p");
$mysqli->query('CREATE PROCEDURE p() READS SQL DATA BEGIN SELECT id FROM test; SELECT id + 1 FROM test; END;');

$mysqli->multi_query("CALL p()");

do {
if (
$result = $mysqli->store_result()) {
printf("---\n");
var_dump($result->fetch_all());
$result->free();
}
} while (
$mysqli->next_result());

以上示例将输出

---
array(3) {
  [0]=>
  array(1) {
    [0]=>
    string(1) "1"
  }
  [1]=>
  array(1) {
    [0]=>
    string(1) "2"
  }
  [2]=>
  array(1) {
    [0]=>
    string(1) "3"
  }
}
---
array(3) {
  [0]=>
  array(1) {
    [0]=>
    string(1) "2"
  }
  [1]=>
  array(1) {
    [0]=>
    string(1) "3"
  }
  [2]=>
  array(1) {
    [0]=>
    string(1) "4"
  }
}

使用预处理语句

使用预处理语句接口从与上面相同的存储过程获取结果时,不需要进行特殊处理。预处理语句和非预处理语句接口类似。请注意,并非每个 MYSQL 服务器版本都支持准备 CALL SQL 语句。

示例 #4 存储过程和预处理语句

<?php

mysqli_report
(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = new mysqli("example.com", "user", "password", "database");

$mysqli->query("DROP TABLE IF EXISTS test");
$mysqli->query("CREATE TABLE test(id INT)");
$mysqli->query("INSERT INTO test(id) VALUES (1), (2), (3)");

$mysqli->query("DROP PROCEDURE IF EXISTS p");
$mysqli->query('CREATE PROCEDURE p() READS SQL DATA BEGIN SELECT id FROM test; SELECT id + 1 FROM test; END;');

$stmt = $mysqli->prepare("CALL p()");

$stmt->execute();

do {
if (
$result = $stmt->get_result()) {
printf("---\n");
var_dump($result->fetch_all());
$result->free();
}
} while (
$stmt->next_result());

以上示例将输出

---
array(3) {
  [0]=>
  array(1) {
    [0]=>
    int(1)
  }
  [1]=>
  array(1) {
    [0]=>
    int(2)
  }
  [2]=>
  array(1) {
    [0]=>
    int(3)
  }
}
---
array(3) {
  [0]=>
  array(1) {
    [0]=>
    int(2)
  }
  [1]=>
  array(1) {
    [0]=>
    int(3)
  }
  [2]=>
  array(1) {
    [0]=>
    int(4)
  }
}

当然,也支持使用绑定 API 进行获取。

示例 #5 使用绑定 API 的存储过程和预处理语句

<?php

mysqli_report
(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = new mysqli("example.com", "user", "password", "database");

$mysqli->query("DROP TABLE IF EXISTS test");
$mysqli->query("CREATE TABLE test(id INT)");
$mysqli->query("INSERT INTO test(id) VALUES (1), (2), (3)");

$mysqli->query("DROP PROCEDURE IF EXISTS p");
$mysqli->query('CREATE PROCEDURE p() READS SQL DATA BEGIN SELECT id FROM test; SELECT id + 1 FROM test; END;');

$stmt = $mysqli->prepare("CALL p()");

$stmt->execute();

do {
if (
$stmt->store_result()) {
$stmt->bind_result($id_out);
while (
$stmt->fetch()) {
echo
"id = $id_out\n";
}
}
} while (
$stmt->next_result());

以上示例将输出

id = 1
id = 2
id = 3
id = 2
id = 3
id = 4

参见

添加注释

用户贡献的注释 1 条注释

Valverde
5 年前
<?php

// 无参数的存储过程调用

$MyConnection = new mysqli ("DB_SERVER", "DB_USER", "DB_PASS", "DB_NAME");

mysqli_multi_query ($MyConnection, "CALL MyStoreProcedure") OR DIE (mysqli_error($MyConnection));

while (
mysqli_more_results($MyConnection)) {

if (
$result = mysqli_store_result($MyConnection)) {

while (
$row = mysqli_fetch_assoc($result)) {

// 例如:DBTableFieldName="userID"
echo "row = ".$row["DBTableFieldName"]."<br />";
....

}
mysqli_free_result($result);
}
mysqli_next_result($conn);

}
?>
*******************************************************************
<?php

// 使用参数的存储过程调用

$MyConnection = new mysqli ("DB_SERVER", "DB_USER", "DB_PASS", "DB_NAME");

mysqli_query($MyConnection ,"SET @p0='".$MyParam1."'");
mysqli_query($MyConnection ,"SET @p1='".$MyParam2."'");
mysqli_multi_query ($MyConnection, "CALL MyStoreProcedure (@p0,@p1)") OR DIE (mysqli_error($MyConnection));

while (
mysqli_more_results($MyConnection)) {

if (
$result = mysqli_store_result($MyConnection)) {

while (
$row = mysqli_fetch_assoc($result)) {

// 例如:DBTableFieldName="userID"
echo "row = ".$row["DBTableFieldName"]."<br />";
....

}
mysqli_free_result($result);
}
mysqli_next_result($conn);

}
?>
To Top