web-dev-qa-db-ja.com

Laravel / EloquentがEagerLoadingにJOINを使用できないのはなぜですか?

<?php

class Cat extends Eloquent {

    public function user() {
        return $this->belongsTo('User');
    }
}

class User extends Eloquent {

    public function cats() {
        return $this->hasMany('Cat');
    }
}

今:

$cats = Cat::with('user')->get();

2つのクエリを実行します。

select * from `cats`
select * from `users` where `users`.`id` in ('1', '2', 'x')

なぜそれができないのですか?

select * from cats inner join users on cats.user_id = users.id

テーブルに両方のid列があると言っている人にとっては、エイリアスを使用すると簡単に回避できます。

select 
    c.id as cats__id,
    c.name as cats__name,
    c.user_id as cats__user_id,
    b.id as users__id,
    b.name as users__name
from cats c
inner join users b on b.id = c.user_id

[〜#〜]更新[〜#〜]

Eloquentはモデルのテーブルの列を知らないと誰かが指摘しましたが、モデルでテーブルを定義する方法を提供できるので、追加のクエリの代わりにエイリアスを使用して適切な結合を実行できると思います。

23
emzero

私の推測では、これにより、複数の1対多の関係を積極的に読み込むことができます。たとえば、犬のテーブルもありました。

class User extends Eloquent {

    public function cats() {
        return $this->hasMany('Cat');
    }

    public function dogs() {
        return $this->hasMany('Dog');
    }
}

ここで、両方をユーザーにロードしたいと思います。

$users = User::with('cats','dogs')->get();

これらを1つのクエリに結合するために機能する結合はありません。ただし、「with」要素ごとに個別のクエリを実行すると、機能します。

select * from `users`
select * from `cats` where `user`.`id` in ('1', '2', 'x')
select * from `dogs` where `user`.`id` in ('1', '2', 'x') 

したがって、この方法論は、いくつかの単純な状況で追加のクエリを生成する可能性がありますが、joinメソッドが失敗するより複雑なデータを熱心にロードする機能を提供します。

これがなぜこのようになっているのかについての私の推測です。

10
DBCrocky

catsusersの両方にidという名前の列があり、提案されたクエリがあいまいなままになっている可能性があります。 Laravelの積極的な読み込みでは追加のクエリを使用しますが、この潜在的な事故を回避します。

2
ceejayoz

LIMITやOFFSETを使用する場合、結合クエリアプローチには致命的な欠点があると思います。

$ users = User :: with( 'cats')-> get()-これにより、以下の2つのクエリが出力されます。

select * from `users`
select * from `cats` where `user`.`id` in ('1', '2', 'x')

そして、その単一のクエリではありません

select * from users inner join cats on cats.user_id = users.id

しかし、たとえば、このレコードセットをページ付けする必要があります。

User :: with( 'cats')-> paginate(10)-これにより、以下の2つのクエリが制限付きで出力されます。

select * from `users` limit 10
select * from `cats` where `user`.`id` in ('1', '2', 'x')

結合すると、次のようになります

select * from users inner join cats on cats.user_id = users.id limit 10

10レコードをフェッチしますが、すべてのユーザーが複数の猫を持つことができるため、10ユーザーを意味するわけではありません。

また、私が思うもう1つの理由は、リレーショナルデータベースとNOSQLデータベースの関係は、分離クエリアプローチで簡単に実装できることです。

また、前の回答と同様に、idはあいまいであり、すべてのステートメントの前に不要なテーブル名を付ける必要があります。

一方、JOINはEXISTSよりもコストが高く、EXISTSはRDBMSにデータをフェッチするように命令せず、関連する行が存在するかどうかを確認するだけなので、高速です。 EXISTSはブール値を返すために使用され、JOINは他のテーブル全体を返します。

シャーディングアーキテクチャに従う場合、スケーラビリティの目的で、JOINを削除する必要があります。これは、スケーリング中にピンタレストによって実践されました。 http://highscalability.com/blog/2013/4/15/scaling-pinterest-from-0-to-10s-of-billions-of-page-views-a.html

1
Azraar Azward