存储过程

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)
  }
}

当然,也支持使用 bind API 进行提取。

示例 #5 使用 bind 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

另见

添加备注

用户贡献备注 2 条备注

6
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);

}
?>
-12
paulo dot reis dot rosa at gmail dot com
7 年前
<?php
/**
* 用于简化带多个参数的调用过程的函数(支持 in/inout/out)
*/
$db = new mysqli('localhost', 'root', 'password', 'database');

$lt_query = callProcedure(
$db,
"stored_procedure",
array(
"in_param1" => "Value1",
"in_param2" => "Value2",
"inout_param3" => "Value3",
"out_param4" => "",
"out_param5" => ""
));

function
callProcedure( $po_db, $pv_proc, $pt_args )
{
if (empty(
$pv_proc) || empty($pt_args))
{
return
false;
}
$lv_call = "CALL `$pv_proc`(";
$lv_select = "SELECT";
$lv_log = "";
foreach(
$pt_args as $lv_key=>$lv_value)
{
$lv_query = "SET @_$lv_key = '$lv_value'";
$lv_log .= $lv_query.";\n";
if (!
$lv_result = $po_db->query($lv_query))
{
/* 写入日志 */
return false;
}
$lv_call .= " @_$lv_key,";
$lv_select .= " @_$lv_key AS $lv_key,";
}
$lv_call = substr($lv_call, 0, -1).")";
$lv_select = substr($lv_select, 0, -1);
$lv_log .= $lv_call;
if (
$lv_result = $po_db->query($lv_call))
{
if(
$lo_result = $po_db->query($lv_select))
{
$lt_result = $lo_result->fetch_assoc();
$lo_result->free();
return
$lt_result;
}
/* 写入日志 */
return false;
}
/* 写入日志 */
return false;
}

/**
* 返回一个类似这样的数组:
*
* $lt_query = array(
* 'in_param1' = 'Value1', // 与调用中的值相同
* 'in_param2' = 'Value2', // 与调用中的值相同
* 'inout_param3' = ?, // 值会相应改变
* 'out_param4' = ?, // 值会相应改变
* 'out_param5' = ? // 值会相应改变
* )
*/
?>
To Top