PHP Conference Japan 2024

PDOStatement::fetch

(PHP 5 >= 5.1.0, PHP 7, PHP 8, PECL pdo >= 0.1.0)

PDOStatement::fetch 從結果集中擷取下一列

說明

public PDOStatement::fetch(int $mode = PDO::FETCH_DEFAULT, int $cursorOrientation = PDO::FETCH_ORI_NEXT, int $cursorOffset = 0): mixed

從與 PDOStatement 物件關聯的結果集中擷取一行。 mode 參數決定 PDO 如何返回該行。

參數

mode

控制如何將下一行返回給呼叫者。此值必須是 PDO::FETCH_* 常數之一,預設為 PDO::ATTR_DEFAULT_FETCH_MODE 的值(預設為 PDO::FETCH_BOTH)。

  • PDO::FETCH_ASSOC:返回以結果集中返回的欄位名稱作為索引的陣列。

  • PDO::FETCH_BOTH(預設):返回以結果集中返回的欄位名稱和從 0 開始的欄位編號作為索引的陣列。

  • PDO::FETCH_BOUND:返回 true,並將結果集中欄位的值賦值給使用 PDOStatement::bindColumn() 方法綁定的 PHP 變數。

  • PDO::FETCH_CLASS:返回所請求類別的新實例,將結果集的欄位映射到類別中具名屬性,並在之後呼叫建構函式,除非同時指定了 PDO::FETCH_PROPS_LATE。如果 mode 包含 PDO::FETCH_CLASSTYPE(例如 PDO::FETCH_CLASS | PDO::FETCH_CLASSTYPE),則類別名稱由第一欄的值決定。

  • PDO::FETCH_INTO:更新所請求類別的現有實例,將結果集的欄位映射到類別中具名屬性。

  • PDO::FETCH_LAZY:結合 PDO::FETCH_BOTHPDO::FETCH_OBJ,返回一個 PDORow 物件,該物件會在存取物件屬性名稱時建立它們。

  • PDO::FETCH_NAMED:返回與 PDO::FETCH_ASSOC 形式相同的陣列,但如果有多個同名欄位,則該鍵所引用的值將是包含該行中所有具有該欄位名稱的值的陣列。

  • PDO::FETCH_NUM:返回以結果集中返回的欄位編號作為索引的陣列,從欄位 0 開始。

  • PDO::FETCH_OBJ:返回一個匿名物件,其屬性名稱對應於結果集中返回的欄位名稱。

  • PDO::FETCH_PROPS_LATE:與 PDO::FETCH_CLASS 一起使用時,會在從相應的欄位值賦值屬性之前呼叫類別的建構函式。

cursorOrientation

對於表示可捲動游標的 PDOStatement 物件,此值決定將哪一行返回給呼叫者。此值必須是 PDO::FETCH_ORI_* 常數之一,預設為 PDO::FETCH_ORI_NEXT。要為 PDOStatement 物件請求可捲動游標,必須在使用 PDO::prepare() 準備 SQL 陳述式時,將 PDO::ATTR_CURSOR 屬性設定為 PDO::CURSOR_SCROLL

cursorOffset

對於 cursorOrientation 參數設定為 PDO::FETCH_ORI_ABS 的可捲動游標的 PDOStatement 物件,此值指定要擷取的結果集中行的絕對編號。

對於 cursorOrientation 參數設定為 PDO::FETCH_ORI_REL 的可捲動游標的 PDOStatement 物件,此值指定相對於呼叫 PDOStatement::fetch() 之前的游標位置要擷取的行。

傳回值

此函式成功時的傳回值取決於擷取類型。在所有情況下,如果失敗或沒有更多行,則返回 false

錯誤/例外

如果屬性 PDO::ATTR_ERRMODE 設定為 PDO::ERRMODE_WARNING,則會發出等級為 E_WARNING 的錯誤。

如果屬性 PDO::ATTR_ERRMODE 設定為 PDO::ERRMODE_EXCEPTION,則會擲出 PDOException 例外。

範例

範例 #1 使用不同的提取樣式提取資料列

<?php
$sth
= $dbh->prepare("SELECT name, colour FROM fruit");
$sth->execute();

/* 測試 PDOStatement::fetch 樣式 */
print "PDO::FETCH_ASSOC: ";
print
"以欄位名稱作為索引的陣列返回下一列\n";
$result = $sth->fetch(PDO::FETCH_ASSOC);
print_r($result);
print
"\n";

print
"PDO::FETCH_BOTH: ";
print
"以欄位名稱和編號作為索引的陣列返回下一列\n";
$result = $sth->fetch(PDO::FETCH_BOTH);
print_r($result);
print
"\n";

print
"PDO::FETCH_LAZY: ";
print
"以 PDORow 物件返回下一列,欄位名稱作為屬性\n";
$result = $sth->fetch(PDO::FETCH_LAZY);
print_r($result);
print
"\n";

print
"PDO::FETCH_OBJ: ";
print
"以匿名物件返回下一列,欄位名稱作為屬性\n";
$result = $sth->fetch(PDO::FETCH_OBJ);
print
$result->name;
print
"\n";
?>

以上範例將輸出

PDO::FETCH_ASSOC: Return next row as an array indexed by column name
Array
(
    [name] => apple
    [colour] => red
)

PDO::FETCH_BOTH: Return next row as an array indexed by both column name and number
Array
(
    [name] => banana
    [0] => banana
    [colour] => yellow
    [1] => yellow
)

PDO::FETCH_LAZY: Return next row as a PDORow object with column names as properties
PDORow Object
(
    [name] => orange
    [colour] => orange
)

PDO::FETCH_OBJ: Return next row as an anonymous object with column names as properties
kiwi

範例 #2 使用可捲動的游標提取資料列

<?php
function readDataForwards($dbh) {
$sql = 'SELECT hand, won, bet FROM mynumbers ORDER BY BET';
$stmt = $dbh->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL));
$stmt->execute();
while (
$row = $stmt->fetch(PDO::FETCH_NUM, PDO::FETCH_ORI_NEXT)) {
$data = $row[0] . "\t" . $row[1] . "\t" . $row[2] . "\n";
print
$data;
}
}
function
readDataBackwards($dbh) {
$sql = 'SELECT hand, won, bet FROM mynumbers ORDER BY bet';
$stmt = $dbh->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL));
$stmt->execute();
$row = $stmt->fetch(PDO::FETCH_NUM, PDO::FETCH_ORI_LAST);
do {
$data = $row[0] . "\t" . $row[1] . "\t" . $row[2] . "\n";
print
$data;
} while (
$row = $stmt->fetch(PDO::FETCH_NUM, PDO::FETCH_ORI_PRIOR));
}

print
"Reading forwards:\n";
readDataForwards($conn);

print
"Reading backwards:\n";
readDataBackwards($conn);
?>

以上範例將輸出

Reading forwards:
21    10    5
16    0     5
19    20    10

Reading backwards:
19    20    10
16    0     5
21    10    5

範例 #3 建構順序

當透過 PDO::FETCH_CLASS 提取物件時,會先指派物件屬性,然後再呼叫類別的建構函式。如果同時指定了 PDO::FETCH_PROPS_LATE,則會反轉此順序,也就是先呼叫建構函式,然後再指派屬性。

<?php
class Person
{
private
$name;

public function
__construct()
{
$this->tell();
}

public function
tell()
{
if (isset(
$this->name)) {
echo
"I am {$this->name}.\n";
} else {
echo
"I don't have a name yet.\n";
}
}
}

$sth = $dbh->query("SELECT * FROM people");
$sth->setFetchMode(PDO::FETCH_CLASS, 'Person');
$person = $sth->fetch();
$person->tell();
$sth->setFetchMode(PDO::FETCH_CLASS|PDO::FETCH_PROPS_LATE, 'Person');
$person = $sth->fetch();
$person->tell();
?>

以上範例的輸出會類似:

I am Alice.
I am Alice.
I don't have a name yet.
I am Bob.

另請參閱

新增註釋

使用者貢獻的註釋 22 則註釋

terry at bitsoup dot com
18 年前
警告
fetch() 在處理空資料集時不遵循 SQL-92 SQLSTATE 標準。

它不會將錯誤碼類別設定為 20 以表示「找不到資料」,而是返回類別 00 表示成功,並向呼叫者返回 NULL。

這也阻止了例外機制的觸發。

程式設計師需要在任何 fetch*() 之後明確編寫測試空結果集的程式碼,而不是依賴 RDBMS 的預設行為。

我嘗試將此記錄為錯誤,但它被駁回為「按預期工作」。只是個提醒。
yarco dot wang at gmail dot com
10 年前
如果沒有記錄,這個函式也會返回 false。
我認為這不太好...
henry at henrysmith dot org
13 年前
有人已經指出 SQLite 驅動程式不支援 PDO::CURSOR_SCROLL。 還值得注意的是,MySQL 驅動程式也不支援它。

實際上,如果您嘗試將可滾動游標與 MySQL 陳述式一起使用,則 PDO::FETCH_ORI_ABS 參數和提供給 fetch() 的偏移量將被靜默忽略。 fetch() 將照常運行,返回資料庫輸出的資料列順序。

一開始,它的行為確實令人困惑。 即使僅作為此頁面上使用者新增的註釋,也絕對值得記錄。
gergo at gergoerdosi dot com
11 年前
在 while 迴圈中使用 PDO::FETCH_COLUMN 時,僅在 while 陳述式中使用該值是不夠的,正如許多範例所示

<?php
while ($row = $stmt->fetch(PDO::FETCH_COLUMN)) {
print
$row;
}
?>

如果存在 5 個值分別為 1 2 0 4 5 的資料列,則上述 while 迴圈將在第三個資料列處停止,僅列印 1 2。 解決方案是明確測試 false

<?php
while (($row = $stmt->fetch(PDO::FETCH_COLUMN)) !== false) {
print
$row;
}
?>

或者使用 foreach 搭配 fetchAll()

<?php
foreach ($stmt->fetchAll(PDO::FETCH_COLUMN) as $row) {
print
$row;
}
?>

兩者都會正確列印 1 2 0 4 5。
lod
16 年前
取得第一個返回項目的快速單行程式碼。這對於非常基本的查詢很有用。

<?php
$count
= current($db->query("select count(*) from table")->fetch());
?>php
public at grik dot net
14 年前
預設情況下,擷取物件時,類別的建構子會在欄位填入值之後被呼叫。

PDO::FETCH_PROPS_LATE 用於改變此行為,使其按預期工作 - 在物件欄位填入資料_之前_呼叫建構子。

範例

<?php
$a
= $PDO->query('select id from table');
$a->setFetchMode(PDO::FETCH_CLASS|PDO::FETCH_PROPS_LATE, 'ClassName');
$obj = $a->fetch();
?>

http://bugs.php.net/bug.php?id=53394
Typer85 at gmail dot com
14 年前
以下提供給使用 PDO SQLite 驅動程式的開發人員一個快速提示

PDO SQLite 驅動程式不支援游標,因此使用 PDO::CURSOR_SCROLL 屬性在使用 PDO SQLite 驅動程式時將無法運作。例如:

<?php

// 假設 $Handle 是一個 PDO 控制代碼。
$Statement = $Handle->query( $sqlStatement , array( PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL ) );

?>

更糟糕的是,即使錯誤模式設定為拋出例外,PDO::prepare 在準備查詢失敗時也不會拋出例外,而是傳回布林值 False!

我認為這不僅是一個糟糕的設計選擇,而且很遺憾的是,這在手冊中沒有任何地方記載 —— 事實上手冊並沒有明確說明哪些驅動程式支援哪些屬性,哪些不支援,因此開發人員只能玩一個經典的猜謎遊戲。

我希望這能為一些開發人員省去一些麻煩。

祝好運,
paulf at quillandmouse dot com
4 年前
先前的發文者指出,當沒有結果時,此函數會傳回 NULL。這是不正確的。此函數會傳回一個空陣列。fetchAll() 也會傳回相同的值。

此外,文件說明了「失敗」時會發生什麼情況,但沒有說明什麼構成「失敗」。「失敗」可能是函數沒有傳回結果的情況;也就是說,查詢「失敗」了。然而,「失敗」顯然是指 PDO 錯誤函數會顯示「失敗」的情況,例如非法的 SQL 語法,或是在不存在的表格上進行查詢等等。空的結果並不是「失敗」。也許這對其他人來說很明顯,但對我來說卻並非如此。
Gerard van Beek
17 年前
如果您要為記錄使用新的類別實例,您可以使用

<?php
include_once("user.class");
$sth = $db->prepare("SELECT * FROM user WHERE id = 1");

/* 自動建立實例 */
$sth->setFetchMode( PDO::FETCH_CLASS, 'user');
$sth->execute();
$user = $sth->fetch( PDO::FETCH_CLASS );
$sth->closeCursor();
print (
$user->id);

/* 或者自行建立一個實例並使用它 */
$user= new user();
$sth->setFetchMode( PDO::FETCH_INTO, $user);
$sth->execute();
$user= $sth->fetch( PDO::FETCH_INTO );
$sth->closeCursor();
print (
$user->id);
?>
lenz_kappov at yahoo dot co dot uk
10 年前
由於 MySQL 目前不支援使用游標,因此在使用 PDO 存取 MySQL 資料庫時,`$cursor_offset` 功能無法運作。

如果您嘗試任意存取 MySQL 資料庫記錄集中的特定記錄或記錄群組,您可以考慮使用 SELECT 陳述式的 LIMIT 子句來達成此目的,例如 `LIMIT 5,3` 只會返回第 6、7 和 8 筆記錄 - 從索引 5(即第 6 筆記錄)開始的 3 筆記錄。
fh at ez dot no
19 年前
我還可以補充一點,如果您使用 PDO::FETCH_CLASS,則在物件設定資料「之後」才會執行建構函式。
fh at ez dot no
19 年前
如果您想使用 PDO::FETCH_CLASS,您需要先使用 setFetchMode 進行設定,如下所示
`$stmt->setFetchMode( PDO::FETCH_CLASS, 'classType', array( '建構函式的參數' ) );`
`$object = $stmt->fetch( PDO::FETCH_CLASS );`
如果您省略這個步驟,PHP 將會發生區段錯誤 (segfault)。
Black Knight
8 年前
這只是關於第二個參數 -cursor_oriantation- 的提醒說明

PDO::FETCH_ORI_NEXT :-
擷取結果集中的下一列。僅適用於可捲動的游標。

PDO::FETCH_ORI_PRIOR :-
擷取結果集中的上一列。僅適用於可捲動的游標。

PDO::FETCH_ORI_FIRST :-
擷取結果集中的第一列。僅適用於可捲動的游標。

PDO::FETCH_ORI_LAST :-
擷取結果集中的最後一列。僅適用於可捲動的游標。

PDO::FETCH_ORI_ABS :-
從結果集中,藉由列號擷取所請求的列。僅適用於可捲動的游標。

PDO::FETCH_ORI_REL :-
從結果集中,藉由相對於游標目前位置的相對位置擷取所請求的列。僅適用於可捲動的游標。
Drew
4 年前
當使用 PDOStatement 並將擷取模式設定為 FETCH_CLASS 時,如果找不到任何列,fetch 將會返回 false。
marcini
17 年前
當您使用預備語句和 MySQL 時,請小心使用 fetch()(我不確定其他資料庫的情況如何)。即使結果集只有一列,Fetch 也不會關閉游標,也不會讓您發送任何其他查詢。
如果您使用 $statement->fetch(),您之後還必須使用 $statement->closeCursor() 才能執行另一個查詢。
或者,您可以使用 $statement->fetchAll() 而無需 $statement->closeCursor()。
Alex
13 年前
如果您使用 INSERT 語句執行 $statement->query(),然後再執行 $statement->fetch(),似乎會出現一個例外,顯示:SQLSTATE[HY000]: 一般錯誤。
lozitskiys at gmail dot com
16 年前
我花了幾個小時試圖找出如何使用 PDO 操作 BLOB 欄位。

請記住,您不能使用以下程式碼擷取 BLOB 資料

<?php
$sql
= 'SELECT * FROM sometable LIMIT 1';
$stmt = $dbh->prepare($sql);
$stmt->execute();
$stmt->setAttribute(PDO::FETCH_ASSOC);
$row = $stmt->fetch();

$myFile = $row['file'];
?>

您應該嘗試以下方法

<?php
$sql
= "SELECT mime, file FROM sometable LIMIT 1";
$stmt = $dbh->prepare($sql);
$stmt->execute();

$stmt->bindColumn(1, $mime,);
$stmt->bindColumn(2, $file, PDO::PARAM_LOB);

$stmt->fetch();

header('Content-type: '.$mime);
print
$file;

?>
tastro
10 年前
請注意,這種方式會覆寫「提取模式」,PDO::FETCH_PROPS_LATE 將不會被應用。

<?php
$sth
= $db->prepare("SELECT * FROM user WHERE id = 1");
$sth->setFetchMode( PDO::FETCH_CLASS|PDO::FETCH_PROPS_LATE, 'user');
$sth->execute();
$user = $sth->fetch( PDO::FETCH_CLASS );
$sth->closeCursor();
?>

相反地,如果您想使用 setFetchMode() 方法設定提取模式,您應該讓 fetch() 方法的參數區域保持空白,像這樣:

<?php
$sth
= $db->prepare("SELECT * FROM user WHERE id = 1");
$sth->setFetchMode( PDO::FETCH_CLASS|PDO::FETCH_PROPS_LATE, 'user');
$sth->execute();
$user = $sth->fetch();
$sth->closeCursor();
?>
sumariva at gmail dot com
12 年前
我可以使用 PDO::FETCH_COLUMN 來取得結果的第一欄。
$ps->fetch( PDO::FETCH_COLUMN );
在使用 PHP 5.3.10 的 Postgresql 上運作正常。
aledmb at gmail dot com
19 年前
請注意,在 5.1 之前的 PHP 版本中,PDO 類別不包含 fetch 常數。
josh
16 年前
請注意,PDO::ATTR_STRINGIFY_FETCHES 不適用於 MySQL 驅動程式。MySQL 永遠會回傳字串,因為這是核心 mysql PHP 擴充功能的行為。請參閱 http://bugs.php.net/bug.php?id=44341
tim at kooky dot org
8 年前
使用 PDO::FETCH_LAZY 時要小心。它會新增一個名為 queryString 的欄位。我不確定這是不是 bug。我使用的是 Debian Jessie 中的 5.6.17 版本。

查詢:'select 1,2,3'

$row=$smt->fetch(PDO::FETCH_OBJ);
var_dump($row);

object(stdClass)#6 (3) {
["1"]=>
string(1) "1"
["2"]=>
string(1) "2"
["3"]=>
string(1) "3"
}

$row=$smt->fetch(PDO::FETCH_LAZY);
var_dump($row);

object(PDORow)#3 (4) {
["queryString"]=>
string(12) "select 1,2,3"
["1"]=>
string(1) "1"
["2"]=>
string(1) "2"
["3"]=>
string(1) "3"
}
To Top