2024 日本 PHP 研討會

儲存程序

MySQL 資料庫支援儲存程序。儲存程序是儲存在資料庫目錄中的子程式。應用程式可以呼叫並執行儲存程序。`CALL` SQL 陳述式用於執行儲存程序。

參數

根據 MySQL 版本的不同,儲存程序可以具有 `IN`、`INOUT` 和 `OUT` 參數。 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

另請參閱

新增註釋

使用者貢獻的註釋 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