警告
fetch() 在處理空資料集時不遵循 SQL-92 SQLSTATE 標準。
它不會將錯誤碼類別設定為 20 以表示「找不到資料」,而是返回類別 00 表示成功,並向呼叫者返回 NULL。
這也阻止了例外機制的觸發。
程式設計師需要在任何 fetch*() 之後明確編寫測試空結果集的程式碼,而不是依賴 RDBMS 的預設行為。
我嘗試將此記錄為錯誤,但它被駁回為「按預期工作」。只是個提醒。
(PHP 5 >= 5.1.0, PHP 7, PHP 8, PECL pdo >= 0.1.0)
PDOStatement::fetch — 從結果集中擷取下一列
$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_BOTH
和 PDO::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.
警告
fetch() 在處理空資料集時不遵循 SQL-92 SQLSTATE 標準。
它不會將錯誤碼類別設定為 20 以表示「找不到資料」,而是返回類別 00 表示成功,並向呼叫者返回 NULL。
這也阻止了例外機制的觸發。
程式設計師需要在任何 fetch*() 之後明確編寫測試空結果集的程式碼,而不是依賴 RDBMS 的預設行為。
我嘗試將此記錄為錯誤,但它被駁回為「按預期工作」。只是個提醒。
有人已經指出 SQLite 驅動程式不支援 PDO::CURSOR_SCROLL。 還值得注意的是,MySQL 驅動程式也不支援它。
實際上,如果您嘗試將可滾動游標與 MySQL 陳述式一起使用,則 PDO::FETCH_ORI_ABS 參數和提供給 fetch() 的偏移量將被靜默忽略。 fetch() 將照常運行,返回資料庫輸出的資料列順序。
一開始,它的行為確實令人困惑。 即使僅作為此頁面上使用者新增的註釋,也絕對值得記錄。
在 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。
取得第一個返回項目的快速單行程式碼。這對於非常基本的查詢很有用。
<?php
$count = current($db->query("select count(*) from table")->fetch());
?>php
預設情況下,擷取物件時,類別的建構子會在欄位填入值之後被呼叫。
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
以下提供給使用 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!
我認為這不僅是一個糟糕的設計選擇,而且很遺憾的是,這在手冊中沒有任何地方記載 —— 事實上手冊並沒有明確說明哪些驅動程式支援哪些屬性,哪些不支援,因此開發人員只能玩一個經典的猜謎遊戲。
我希望這能為一些開發人員省去一些麻煩。
祝好運,
先前的發文者指出,當沒有結果時,此函數會傳回 NULL。這是不正確的。此函數會傳回一個空陣列。fetchAll() 也會傳回相同的值。
此外,文件說明了「失敗」時會發生什麼情況,但沒有說明什麼構成「失敗」。「失敗」可能是函數沒有傳回結果的情況;也就是說,查詢「失敗」了。然而,「失敗」顯然是指 PDO 錯誤函數會顯示「失敗」的情況,例如非法的 SQL 語法,或是在不存在的表格上進行查詢等等。空的結果並不是「失敗」。也許這對其他人來說很明顯,但對我來說卻並非如此。
如果您要為記錄使用新的類別實例,您可以使用
<?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);
?>
由於 MySQL 目前不支援使用游標,因此在使用 PDO 存取 MySQL 資料庫時,`$cursor_offset` 功能無法運作。
如果您嘗試任意存取 MySQL 資料庫記錄集中的特定記錄或記錄群組,您可以考慮使用 SELECT 陳述式的 LIMIT 子句來達成此目的,例如 `LIMIT 5,3` 只會返回第 6、7 和 8 筆記錄 - 從索引 5(即第 6 筆記錄)開始的 3 筆記錄。
如果您想使用 PDO::FETCH_CLASS,您需要先使用 setFetchMode 進行設定,如下所示
`$stmt->setFetchMode( PDO::FETCH_CLASS, 'classType', array( '建構函式的參數' ) );`
`$object = $stmt->fetch( PDO::FETCH_CLASS );`
如果您省略這個步驟,PHP 將會發生區段錯誤 (segfault)。
這只是關於第二個參數 -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 :-
從結果集中,藉由相對於游標目前位置的相對位置擷取所請求的列。僅適用於可捲動的游標。
當您使用預備語句和 MySQL 時,請小心使用 fetch()(我不確定其他資料庫的情況如何)。即使結果集只有一列,Fetch 也不會關閉游標,也不會讓您發送任何其他查詢。
如果您使用 $statement->fetch(),您之後還必須使用 $statement->closeCursor() 才能執行另一個查詢。
或者,您可以使用 $statement->fetchAll() 而無需 $statement->closeCursor()。
如果您使用 INSERT 語句執行 $statement->query(),然後再執行 $statement->fetch(),似乎會出現一個例外,顯示:SQLSTATE[HY000]: 一般錯誤。
我花了幾個小時試圖找出如何使用 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;
?>
請注意,這種方式會覆寫「提取模式」,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();
?>
我可以使用 PDO::FETCH_COLUMN 來取得結果的第一欄。
$ps->fetch( PDO::FETCH_COLUMN );
在使用 PHP 5.3.10 的 Postgresql 上運作正常。
請注意,PDO::ATTR_STRINGIFY_FETCHES 不適用於 MySQL 驅動程式。MySQL 永遠會回傳字串,因為這是核心 mysql PHP 擴充功能的行為。請參閱 http://bugs.php.net/bug.php?id=44341
使用 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"
}