ActiveRecord

Why when i do

$lang = CoreLang::model()->findAll();

when i do var_dump($lang); it returns so much information the page takes couple of minutes to load. Why does the active record do all of that when i only asked for the rows.

How many records does that atually return?

ActiveRecord stores all the info about the table structure in each record. This is not very efficient but I can’t really see it working any other way.

It returns the right amount of records. But with each records set it returns EVERYTHING about the DB, the table, columns, relations etc… question is, Is there a way to explicitly set to return ONLY the rows and nothing else? Or returning them as an associative array?




return $this->dbConnection->createCommand("SELECT * FROM coreLang")->query();



Yea i know that but i would like to do that using the active record.

Each AR object contains a reference to the underlying table schema object. Each table only has only schema object. So the only extra space needed is that storing an object reference.

You may use $record->attributes to retrieve the associative array representation of the AR object.

And if you want to do it in one line:


array_map(create_function('$x','return $x->attributes;'), ModelClass::model()->findAll())

I know about the ways it can be done, Just thought implementing something by default will make it easier to who ever would like to use it. As in this example for instance. My main concern is that all of that extra data returned in the query will not make lags when we will be using large queries…Any idea about this? Qiang?

actually it appears that there is quit a bit more stuff that needs some memory.

I just did a test retreiving 20,000 identical records from my database.




		$start = microtime(true);

		$p = Product::model()->findAll();

		$end = microtime(true);

		echo $end-$start;

		echo "\n<br>";

		echo memory_get_peak_usage();

		echo "\n<br>";

		echo count($p);



returns

1.46954798698

48811720

20003

so it’s pretty fast but needs a ton of memory

this code




		for($i=0;$i<20000;$i++) {

			$p[] = array(

				'id' => 8888,

				'title' => 'sdfsfsf',

				'description' => 'blah blah blah',

				'created' => '',

				'updated' => '',

				'active' => 1

			);

		}

		echo memory_get_peak_usage();



prints this

16722072

20000

so 20k records in an array need about 30MB less memory

This the price to be paid for working with pretty objects. Anyone who doesn’t like that has to use cakephp and have fun with Array-relational-mapping instead of object-relational mapping :)

That’s not fair comparison. You should measure the peak memory before and after findAll() and get the difference. Same thing for the array test.

Even that is not fair enough because the first one would involve loading PDO extension, instantiating schema objects etc.

So a better approach is to call findAll() first. Then get peak memory X. Then findAll() again and get peak memory Y again. The value Y-X should be very close to the actual memory needed by AR objects.

Could you share your findings? Thanks!




		echo memory_get_peak_usage()."\n<br>";

		$p = Product::model()->findAll();

		$end = microtime(true);

		echo memory_get_peak_usage()."\n<br>";

		$p2 = Product::model()->findAll();

		$end = microtime(true);

		echo memory_get_peak_usage();



returns

2529024

48825320

82026760

So according to this the 20k records need about 33MB

What about the array version? Does findAll() give the same column values as in your array version?

This number seems more realistic. For each AR object, there are five extra member variables, which should be the cause of the memory overhead.

in comparison




		echo memory_get_peak_usage()."\n<br>";

		$p3 = Yii::app()->db->createCommand("SELECT * FROM product")->query()->readAll();

		echo memory_get_peak_usage()."\n<br>";

		$p4 = Yii::app()->db->createCommand("SELECT * FROM product")->query()->readAll();

		echo memory_get_peak_usage()."\n<br>";



2530024

18630536

34445048

qiang,

On the project I’m working on right now I’ll soon run into a situation where I’ll have to fetch a large number of records.

Is there a way I can use Yii to generate the query for me, take care of all the joins and so on but then I execute it manually using the method above?

thanks

Thanks for your profiling work.

Nope, we don’t plan to support generating SQLs by AR. If you want to optimize your code by eliminating AR, you can first write an AR version, then use the weblog to obtain the generated SQLs, and then replace AR code with those plain SQL executions. This would be faster than relying on AR to generate SQLs.

Also, the overhead in memory of AR remains constant and does not grow with column numbers.


Also, the overhead in memory of AR remains constant and does not grow with column numbers. 

But it does with row numbers.