AR should apply LIMIT 1 when lazy loading HAS_ONE relations #1857

Closed
samdark opened this Issue Dec 14, 2012 · 8 comments

Comments

Projects
None yet
4 participants
Owner

samdark commented Dec 14, 2012

Post relation:

'lastComment' => array(self::HAS_ONE, 'Comment', 'post_id', 'order' => 'lastComment.id DESC')

Controller:

$post = Post::model()->find();
$lastComment = $post->lastComment;

SQL generated:

SELECT `lastComment`.`id` AS `t1_c0`, `lastComment`.`content` AS `t1_c1`, `lastComment`.`post_id` AS `t1_c2`
FROM `tbl_comment` `lastComment`
WHERE (`lastComment`.`post_id`=:ypl0)
ORDER BY lastComment.id DESC)

Since we need only one record LIMIT 1 should be applied.

Very minimal test application:

http://ds.rmcreative.ru/ar_has_one_limit_issue.zip

tomtomsen pushed a commit to tomtomsen/yii that referenced this issue Feb 14, 2013

tomtomsen pushed a commit to tomtomsen/yii that referenced this issue Feb 14, 2013

Member

klimov-paul commented Mar 20, 2013

On my opinion, the issue is inconsistent.

The "HAS_ONE" relation is the one, which supports value extraction from related table by the single query.

If you run:

Item::model()->with(‘category’)->find();

Active Record will fetch the data for “Item” and “Category” by the single query, like following:

SELECT i.*,c.*
FROM item AS i
INNER JOIN category AS c ON i.category_id = c.id

If we satisfy the demand of this issue and allow to specify order and limit for the “HAS_ONE” relation, how we will resolve the “with” option?

@tomtomsen, if you want to find latest comment for the post, you should declare the relation “posts” as “HAS_MANY” and then use the method notation on its lazy load:

$post = Post::model()->findByPk($id);
$lastComment = $post->comments(array(‘order’=>’id DESC’, ‘limit’=>1));
Owner

qiangxue commented May 27, 2013

Closed for the reasons explained.

@qiangxue qiangxue closed this May 27, 2013

Contributor

magefad commented Jun 27, 2013

http://www.yiiframework.ru/forum/viewtopic.php?f=3&t=12859

array(‘order=>id DESC’, ‘limit=>1)

not working in model search() (not lazy load). Any solutions?

Member

klimov-paul commented Jun 27, 2013

There is not way to find “users” with their last “order” using eager loading. There is not SQL query which can perform such selection.
Last “order": of the user can be found only via lazy loading.

Contributor

magefad commented Jun 27, 2013

In CGridView will be hundreds of SQL queries..

Member

klimov-paul commented Jun 27, 2013

In your particular case you may attempt to solve the situation using “denormalize” approach.
You can add an extra field “is_last” to your “order” table and manually set it to “1” on every new user order, while settting “0” for existing one.
Then you can join the table “order” with the “user” as “HAS_ONE” using condition “order.user_id = user.id AND order.is_last = 1”.

Contributor

magefad commented Jun 27, 2013

I thought about this solution, but it is not very good: (

Member

klimov-paul commented Jun 27, 2013

Sorry, but there is nothing else I could advise you.
Extracting last record from the slave table for each record from master table is impossible.
This has nothing to do with Yii or ActiveRecord – it is a limitation of SQL, or to be more precise the limitation of the relational database.
You should either refactor your DB structure and logic to “hack” the problem or switch to some not relational data storage.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment