laravel6.0でのsubQueryの書き方(+subQuery　vs　EagerLoadingについて)

#　はじめに
　laravel6.0が出て数日たちましたね。
　今までsubQueryどころかEagerLoadingすらも面倒だからいいやとサボっていた人間なので、
　この機会に少しまとめておきたいと思います。

# 　laravel6.0でのsubQuery
 
　今usersとbooksがあるとして

  ```
    function ($query) {
            $query->select("name")->from("books")->whereColumn("user_id", "users.id")
                ->limit(1);
    }
  ```
 　これをaddSelectや、orderByに入れるだけです。

 ```
  $users = User::addSelect(["book_name" => function ($query) {
            $query->select("name")->from("books")->whereColumn("user_id", "users.id")
                ->limit(1);
        }])->get();

　foreach ($users as $user) {
            $name = $user->book_name;
        }
```

　こんな感じです。


#　何もしない　vs　EagerLoading　vs　subQuery


![user_book_er.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/496421/c5f8232e-801f-aacd-09f3-1fcfb43fe681.png)
　


 　controllerを作ってそこにアクセスして比較していきます。

```php:SampleController.php

  class SampleController extends Controller
    {

    public function index()
    {
        $start = microtime(true);
        $memory = memory_get_usage();

        $this->getBooks();

        $result = [
            "time" => microtime(true) - $start,
            "memory" => (memory_get_peak_usage() - $memory) / (1024 * 1024)
        ];

        dump($result);
    }

    public function getBooks()
    {

        //optionalはuserに対応するbookが必ず存在するとは限らないためつけてます。       

        //通常
        $users = User::all();
        foreach ($users as $user) {
            $name = optional($user->books->first())->name;
        }

        //EagerLoading
        $users = User::with("books")->get();
        foreach ($users as $user) {
            //リレーションメソッドにしてしまうとeagerloadingの効果が失われてしまうので気を付けて
            $name = optional($user->books->first())->name;
        }

        
        //subQuery
        //addSelectによりusersのすべてと、book_nameが選択されている状態
        $users = User::addSelect(["book_name" => function ($query) {
            $query->select("name")->from("books")->whereColumn("user_id", "users.id")
                ->limit(1);
        }])->get();

        foreach ($users as $user) {
            $name = $user->book_name;
            
        }
    }
}
   ```


 　**user 1000件　book　1000件の場合**
 
|method  |time  |memory  |
|---|---|---|
|通常  |2.1359429359436  |4.7769012451172  |
|EagerLoading  | 0.242516040802  |4.222671508789  |
|subQuery  |0.46214890480042  |3.937858581543  |

　**user 3000件　book　3000件の場合**
 
|method  |time  |memory  |
|---|---|---|
|通常  |8.952919960022  |13.542144775391  |
|EagerLoading  | 0.79955101013184  |11.902877807617  |
|subQuery  |3.0595591068268  |10.085777282715  |


　**user 5000件　book　5000件の場合**
 
|method  |time  |memory  |
|---|---|---|
|通常  |19.71000289917  |28.348297119141  |
|EagerLoading  |  1.9218430519104  |25.727615356445  |
|subQuery  |6.256618976593  |16.407073974609 |

　**user 10000件　book　10000件の場合**
 　通常は60秒を超えてtimeoutでした。

|method  |time  |memory  |
|---|---|---|
|通常  |  |  |
|EagerLoading  |  3.9436728954315  |50.53173828125  |
|subQuery  |24.066353082657  | 31.92147064209 |

#　まとめ
　　上の結果だけ見るとEagerLoading一択みたいに見えるんですけど、
　　書き方や計測方法が悪いだけなんですかね・・・？
　　memory的にはやっぱりsubQueryがいい感じなんだけども・・・
　　おそらくどこか間違っていると思うので詳しい方いたら指摘していただければ助かります！