PHP PDO fetchAll() - while循环不起作用,但foreach可以工作

8

我想知道我是否做得很好,或者说fetchAll()函数不能和WHILE一起使用。

以下是一个例子:

$db=new PDO("mysql:host=" .$dbhost. "; dbname=" . $dbname, $dbuser, $dbpass);

$page=$db->prepare("SELECT * FROM page");
$page->execute();

foreach ($page->fetchAll(PDO::FETCH_ASSOC) as $row) {

//echo a row
//is working
}

然而,如果我尝试使用while循环进行迭代
while ($row=$page->fetchAll(PDO::FETCH_ASSOC)){

//echo a row
//Show empty
}

我尝试只使用fetch(),它可以正常工作,我的问题是:为什么使用“WHILE”时fetchAll()不能正常工作?

在 while 循环示例中,你可能应该使用 fetch 而不是 fetchAll。在 for 循环中,你应该在循环条件之前获取所有内容。fetchAll 会将所有匹配的行作为数组或行数组返回。 - Orangepill
是的,fetch() 正常工作,但我的数据库很大,我认为最好的方法是使用 fetchAll()。 - Mafitsi
5个回答

21

Fetch all返回结果集中剩余的所有记录。因此,您的foreach循环可以按预期迭代结果集。

对于等效的while实现,应该使用$page->fetch(PDO::FETCH_ASSOC);

while ($row = $page->fetch(PDO::FETCH_ASSOC)){
   // do something awesome with row
} 

如果您想使用 while 循环并获取所有内容,可以这样做:

$rows = $page->fetchAll(PDO::FETCH_ASSOC);

// use array_shift to free up the memory associated with the record as we deal with it
while($row = array_shift($rows)){
   // do something awesome with row
}

不过需要提醒的是:如果结果集很大,fetch all将会完全获取所有数据,这将会对您的机器资源造成压力。我只建议在确定结果集较小或通过查询应用限制时再使用该方法。


对于那些已经习惯使用 while 循环或者他们的代码已经在使用 while 循环的人,如果他们想将他们的 MySQL 类升级为 PDO,我会建议这个解决方案,因为你不需要改变你的编码风格。 - AkiEru
是的,array_shift() 可以与 while()fetchAll() 一起使用。但是,为什么要这样做呢?使用 foreach()fetchAll() 有什么问题吗?在这种情况下,使用 while()foreach() 有什么好处? - stack
@stack 唯一的真正好处是,通过使用 array_shift,你可以潜在地更快地向服务器释放资源。 - Orangepill
我总是喜欢知道这种小巧而聪明的细节。非常感谢。+1 (顺便说一句,我是在PHP房间里担心一个if语句的那个人 ;-)) - stack

8

来自 PHP 手册:

while 语句的意思很简单。只要 while 表达式的值为 TRUE,PHP 就会重复执行嵌套语句。

由于你使用的方法返回一个数组,while ($row=$page->fetchAll(PDO::FETCH_ASSOC))会将 $row 设置为整个结果集的数组。你期望的是 fetchAll 方法扩展Iterator 类,但它并没有这样做。

在这里使用 foreach 是正确的方法。


6
无需遍历记录集,因为fetchAll会一次性获取所有的记录。很不错,是吧?
$rows = $page->fetchAll(PDO::FETCH_ASSOC);

// $rows is an array containing all records...
foreach ($rows as $row)
    echo $row->fieldname;

谢谢@michi,不过有一个问题,它在我的本地服务器上工作正常,但当我上传到托管时就无法工作了:( - yaqoob
我写了这个超过4年了;-) 本地机器和服务器上的不同设置,使用谷歌系统地解决此问题。 - michi
找到问题了,我在 SQL 重命名时将表名大写了,改成小写就可以了 :p - yaqoob
太棒了!简单而有效的解决方案 :) - Jodyshop

2
我尝试重现您的情况。请看这里:

script.php

<?php
$host = 'localhost';
$user = "user";
$password = '';
$db_name = 'test';
$port = 3306;

try
{
    $connection = new PDO("mysql:host=$host;port=$port;dbname=$db_name", $user, $password);
    $connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
catch (PDOException $e)
{
    echo 'Connection failed: ' . $e->getMessage();
}

$page=$connection->prepare("SELECT * FROM Document");
$page->execute();

while ($row = $page->fetchAll(PDO::FETCH_ASSOC)) {
    var_dump($row);
}

数据库测试

DROP TABLE IF EXISTS `Document`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `Document` (
  `DataID` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `Description` varchar(50) CHARACTER SET utf8 NOT NULL,
  PRIMARY KEY (`DataID`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `Document`
--

LOCK TABLES `Document` WRITE;
/*!40000 ALTER TABLE `Document` DISABLE KEYS */;
INSERT INTO `Document` VALUES (1,'!!!'),(2,'This is document 2'),(3,'This is document 3'),(4,'This is document 4'),(5,'Hello');
/*!40000 ALTER TABLE `Document` ENABLE KEYS */;
UNLOCK TABLES;

输出

$php script.php
array(5) {
  [0]=>
  array(2) {
    ["DataID"]=>
    string(1) "1"
    ["Description"]=>
    string(3) "!!!"
  }
  [1]=>
  array(2) {
    ["DataID"]=>
    string(1) "2"
    ["Description"]=>
    string(18) "This is document 2"
  }
  [2]=>
  array(2) {
    ["DataID"]=>
    string(1) "3"
    ["Description"]=>
    string(18) "This is document 3"
  }
  [3]=>
  array(2) {
    ["DataID"]=>
    string(1) "4"
    ["Description"]=>
    string(18) "This is document 4"
  }
  [4]=>
  array(2) {
    ["DataID"]=>
    string(1) "5"
    ["Description"]=>
    string(5) "Hello"
  }
}

输出结果表示,当语句执行一次并打印出查询应该返回的所有行时,这是完全正确的,因为fetchAll返回一个包含所有行的数组。PHP将其解释为true并运行while循环。

foreach会迭代数组中的每个数组,并且每次都会得到相应的行。


0

通过在 while 循环中使用 fetchAll 方法,您已经在第一次迭代中获取了所有记录,因此下一次没有任何数据可获取。虽然在 foreach 中,在第一行也获取了所有的记录,但是 foreach 用于迭代结果。


那么在这种情况下唯一的解决方案就是使用foreach()吗? - Mafitsi
@Maftisi,我发布了一个解决方案,它使用while循环处理通过fetchAll获取的结果集。 - Orangepill
@Mafitsi,你可以使用fetchAllforeach或者使用while循环中的fetchAll结果,就像Orangepill的解决方案一样。while需要在每次迭代时评估条件。 - bansi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接