2017-06-13 49 views
7

mam następujące modele: tektura, użytkownik, UserPricingPlans, PricingPlanLimitswymowny Relacje Optymalizacja zapytań

Uwaga: nie przeszkadza, jeśli coś jest nie tak z modeli code.They działają bez zarzutu.

Tektura

class CardBoard extends Model{ 

    public function user(){ 

     return $this->belongsTo('Models\User','id_user'); 

    } 
} 

użytkownika

class User extends Model{ 

    public function pricingPlans(){ 

     return $this->hasMany('Models\UserPricingPlan','id_user'); 

    } 
} 

PricingPlan

class PricingPlan extends Model{ 

    public function limits(){ 

     return $this->hasOne('Models\PricingPlanLimits','id_pricing_plan','id_pricing_plan'); 

    } 
} 

CenyPlan Limit

Nie opiszę tego modelu, nie jest on niezbędny do rozwiązania problemu. Należy jednak pamiętać, że istnieje atrybut o nazwie maxBoards.

Problem polega na tym, że mam tylko Tektura modelu wystąpienie do pracy i chcę dostać maxBoard atrybut z PricingPlanLImits. Zrobiłem to tak:

Uwaga: Mam już tutaj instancję modelu CardBoard! powyżej

$maxBoard = $cardBoard->user->pricingPlans->last()->limits->maxBoard; 

return $maxBoard; 

Kod działa świetnie, ale liczba zapytań generowanych przez tę operację jest napowietrznych do mnie. Wymyślne wywołanie SELECT dla nazwanych i nie chcę wszystkich tych danych i operacji.

{ 
    "query": "select * from `users` where `users`.`id_user` = ? limit 1", 
    "bindings": [ 
    ], 
    "time": 0.96 
} 
{ 
    "query": "select * from `users_princing_plan` where `users_princing_plan`.`id_user` = ? and `users_princing_plan`.`id_user` is not null", 
    "bindings": [ 
    ], 
    "time": 0.8 
} 
{ 
    "query": "select * from `pricing_plan_limits` where `pricing_plan_limits`.`id_pricing_plan` = ? and `pricing_plan_limits`.`id_pricing_plan` is not null limit 1", 
    "bindings": [ 

    ], 
    "time": 0.88 
} 

Nie ma sposobu na optmizację tego i uruchamianie mniejszej liczby zapytań w trybie Eloquent-Way?

+0

Witam! Dlaczego twoje instrukcje SQL są wykonywane w 0,96 sekundy? (wydaje się zbyt długi) Jaki typ bazy danych posiadasz? Czy skonfigurowałeś indeksy swoich kolumn? – YanDatsyuk

+0

Wymieniłem oryginalny dziennik, aby nie pokazywał niczego ważnego. Ta kwerenda trwa 0,64 s, baza danych używa INNODB (MySQL) jako Engine i działa na lokalnym improwizowanym lokalnym serwerze tylko do celów testowych. id_user jest kluczem podstawowym. –

+0

Jeśli serwer był na dysku SSD, czas zapytania byłby szybszy. –

Odpowiedz

0

możesz uzyskać dane w jednym zapytaniu, jeśli używasz metody with().

np: CardBoard::with('user.pricingPlans')->get();

więc można zoptymalizować zapytanie za pomocą with metody.

+0

** with ** sprawia, że ​​kwerenda CardBoard Model Query i wykonuje wszystkie powyższe zapytania, w rzeczywistości go pogarsza, ponieważ dodaje jeszcze jedno zapytanie ... Mam już instancję Card Card Design, używając ** obciążenia ** było lepiej, ponieważ używa już stworzonego modelu. –

+0

Chcę pewnego rodzaju INNER JOIN, aby uniknąć wszystkich tych wyborów i zmniejszyć 3 kwerendy do 2 lub nawet 1, ponieważ wszystkie te modele mają zagnieżdżoną relację. –

0

poprzednie komentarze nie były zbyt istotne dla tego rozwiązania ...

przykład

$cardboard->user()->whereHas('pricingPlans', function ($plans) { 
    $plans->selectRaw('price_plan_limits.id, MAX(price_plan_limits.maxBoard) as MB')) 
      ->from('price_plan_limits') 
      ->where('price_plan_limits.id', 'price_plan.id') 
      ->orderBy('MB', 'DESC') 
})->get(); 
+0

Czy możesz podać przykład używając powyższych modeli? –

+0

Zobacz edytuj w mojej odpowiedzi. – btl

+0

Spróbuję to zaimplementować, ale wolę korzystać z Epojedynczych Pivot (niestandardowe modele stołów pośrednich). Myślę, że rozwiązanie będzie o wiele bardziej przejrzyste. –

0

I zwykle w odwrotnej kolejności:

$maxBoard = PricingPlanLimits::whereHas(function($q)use($cardBoard){ 
    $q->whereHas('PricingPlan', function($q1)use($cardBoard){ 
     $q1->whereHas('User', function($q2)use($cardBoard){ 
      $q2->whereHas('CardBoard', function($q3)use($cardBoard){ 
       $q3->where('id', $cardBoard['id']); 
      }); 
     }); 
     // Probably you have to improve this logic 
     // It is meant to select select the last occurrence 
     $q1->orderBy('created_at', 'desc'); 
     $q1->limit(1); 
    }); 
})->first()['maxBoard']; 

Całkowicie niesprawdzonych, ale to powinno być właściwym podejściem do osiągnięcia celu w jednym zapytaniu.

0

Prawdopodobnie można zmniejszyć liczbę połączeń za pomocą relacji maManyThrough (Patrz: https://laravel.com/docs/5.4/eloquent-relationships#has-many-through).

W tym przypadku trzeba było coś takiego

class CardBoard extends Model{ 

    public function userPricingPlans(){ 

     return $this->hasManyThrough('Models\UserPricingPlan', 'Models\User', 'id_user', 'id_user'); 
    } 
} 

I wtedy można nazwać tak:

$maxBoard = $cardBoard->userPricingPlans->last()->limits->maxBoard; 

Aby mieć to wszystko w jednym zapytaniu, że trzeba płynnie i prawdziwe sprzężenia SQL, nie da się zrobić z wymową (ale wtedy stracisz całą zabawę z ORM)