AR是否能添加一个fetch_array功能?

CActiveRecord中查询提供的findAll功能,只能一次性取出所有记录,这是一个很废内存的操作,稍有不慎就会内存溢出(超过php.ini中设定的memory_size)

有一个表,共27w数据,并不大,我想遍历一次,针对每一个row,做一些操作,用raw PHP很容易实现:




<?php

    mysql_connect('192.168.1.123:3306', 'user', 'xxxx') or die("Could not connect: " . mysql_error());

    mysql_select_db("my_schema");

    mysql_set_charset("utf8");

    $result = mysql_query("select * from prop_value");

    while ($row = mysql_fetch_assoc($result)) {

        $tmp = $row->vid + $row->pid;

        //bla bla

    }

    



上述代码run得很畅快,因为它每次取一行数据,不会有内存溢出问题。

而在AR中没有相应的操作,findAll只能一次性取出所有数据,于是:




    public function run($args)

    {

        $values = PropValue::model()->findAll();

        foreach ($values as $value) {

            $tmp = $value->vid + $value->pid;

        }

    }



这就直接挂了,memory_size=128M,不够。。

本来以为是AR文档不够,应该有相应的方法可以遍历结果集而不用全部load到内存,但查看源码发现findAll函数调用了




//in findAll last line:

return $this->query($criteria,true);

//in query:

private function query($criteria,$all=false){

  //...

  return $all ? $this->populateRecords($command->queryAll()) : $this->populateRecord($command->queryRow());

}

//你看到了,就是调用了populateRecords($command->queryAll())

//再看populateRecords:

public function populateRecords($data,$callAfterFind=true)

  //...

  foreach($data as $attributes){

    //...

  }

}



绝望,在findAll函数里,实际上已经把数据库中所有行都预先取出来遍历了一次,把你整合成一个array传回……

难道遍历一个大一点的表(何况27w条数据的表并不大)不是一个常见操作吗?遇到这种情况,我只能分次limit??

期待qiang的解答,希望能提供fetch row by row的DB调用方式,谢谢~~

等了一天也没人回答,各位大牛哪里去了啊?

Yii包装精良,extension现在也不少了,不想放弃这个框架,期待达人解答!

暂时先用裸的PDO解决一下了:




        $dbh = Yii::app()->db->getPdoInstance();

        $sth = $dbh->prepare("select * from prop_value");

        $sth->execute();

        while($row = $sth->fetch()){

            echo $row['prop_name'],"\n";

        }



这种代码运行时内存使用60M

如果是以下代码:




$values = PropValue::model()->findAll();



肯定就爆了。

提供一个包装了PDO的fetch方法的函数应该不麻烦,希望能有个patch出来。

实际上页面上不会需要遍历整个数据库,应该还是会有limit的,不过在console中就不一样了。现在是在学习Yii的console框架,感觉数据库访问是一个亟待解决的问题。

既然你想利用fetch_array()的式样,又想是单个单个的循环读取,那为什么不用DAO的CDbDataReader?

如果是AR,那么你用fetch_array()有干什么,一次读取一个对象?

终于有回答了,不错。

我也刚刚看到了CDbDataReader,经过测试,的确是row by row的模式,不过返回的不是对象,只是数组了,难道一次取一行就不能读取一个对象?既然做了面向对象,为何要分这种情况呢?感觉很奇怪。

以下代码可行:




   $command=Yii::app()->db->createCommand("select * from prop_value");

   $command->execute();   // a non-query SQL statement execution

   // or execute an SQL query and fetch the result set

   $reader=$command->query();

   foreach($reader as $row){

       print_r($row);//array only,not object

   }



而且使用了CDbDataReader,就是直接操作CDbConnection,要使用Criteria还挺麻烦,为什么这里要搞两套东西呢?

我看不出对象化和逐行读取有什么冲突啊?

为什么搞两套呢?

AR在做一些轻捷的任务时很方便,但是与DAO比起来,从效率上讲,以及复杂的统计的功能,它都还有些欠缺。

如果想在AR上实现step by step 的功能逐行取,也许你可以用PDO的fetch(PDO::FETCH_OBJ)的方法去增加/修改一个方法(感觉AR很强大,用单纯的fetch(PDO::FETCH_OBJ)的很难去维护一个完整的AR)。不管怎样框架又特别是Yii给了你很大的灵活性和预留接口,方便实现自己的功能。(当然这个我也没有去做).

现在很少碰到逐条读取记录的时候了。而且总是会碰到分页读取,感觉没有必要担心。即使以循环一条一条读取记录的方法,碰到如上所述的较多记录时,该方法也不怎么较好,毕竟循环读取的次数有点多吧。

这种情况你可以考虑直接用DAO。如果希望用AR的话,那么需要分页分批处理(比如每次循环处理1000条)。