Warum kann Laravel/Eloquent JOIN nicht für Eager Loading verwenden?
Lesezeit: 6 Minuten
empz
<?php
class Cat extends Eloquent {
public function user() {
return $this->belongsTo('User');
}
}
class User extends Eloquent {
public function cats() {
return $this->hasMany('Cat');
}
}
Jetzt:
$cats = Cat::with('user')->get();
Führt 2 Abfragen aus:
select * from `cats`
select * from `users` where `users`.`id` in ('1', '2', 'x')
Warum kann es nicht einfach:
select * from cats inner join users on cats.user_id = users.id
Für diejenigen, die sagen, dass es beide ID-Spalten in der Tabelle gibt, könnte dies mit Aliasen leicht vermieden werden:
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
AKTUALISIEREN
Jemand wies darauf hin, dass Eloquent die Spalten der Tabellen aus den Modellen nicht kennt, aber ich denke, sie könnten eine Möglichkeit bieten, sie im Modell zu definieren, damit es Aliase verwenden und anstelle einer zusätzlichen Abfrage eine ordnungsgemäße Verknüpfung durchführen könnte.
@deczo Ahmmm … weil es 1 Abfrage statt 2 ist? Das user_id an Cat ist nicht nullable, every Cat gehört zu einem User… Also warum nicht inner join?
– empz
28. Mai 2014 um 20:26 Uhr
@deczo Auch dies ist kein großes Problem, wenn die ORM-Designer gesucht es anzugehen. Die Definition der Objekte könnte leicht zwischen „hat immer“ und „manchmal hat“ oder dem Abruf „auf jeden Fall mit“ und „möglicherweise mit“ unterscheiden und entsprechend innere, linke oder rechte Verknüpfung auswählen. Ich kenne Eloquent nicht, also haben sie vielleicht einfach beschlossen, die Dinge einfach zu halten.
– IMSoP
28. Mai 2014 um 21:09 Uhr
@deczo Ich glaube nicht, dass ich dir folge. Die Join-Klausel existiert aus einem bestimmten Grund, sodass Sie keine zusätzlichen Abfragen durchführen müssen, um Zeilen aus verschiedenen Tabellen abzugleichen. Wenn Sie zwischen verschiedenen Arten von Joins sprechen, wie left, right, inner… Ich denke, Sie könnten Eloquent sagen, welche je nach Beziehung im Modell verwendet werden soll. belongsTo würde bedeuten, dass es immer gehört, also kann der Fremdschlüssel nicht null sein. Sie könnten ein weiteres Like hinzufügen canBelongTo Dies würde zulassen, dass der Fremdschlüssel null ist, und aus diesem Grund würde er die linke Verknüpfung verwenden. Weiß nicht, sag nur…
– empz
28. Mai 2014 um 21:10 Uhr
Die Sache ist, dass ORM nur eine weitere Ebene ist, um das Leben einfacher zu machen. Sein Hauptzweck ist es, entwicklerfreundlich zu sein, und ich denke, das ist so einfach wie es nur sein kann. Es hat Fehler und Einschränkungen, es gibt Leistungsprobleme, auch Inkonsistenzen im Code, aber andererseits ist es wirklich eloquent und großartig für einfache Aufgaben. Allerdings gibt es kein ORM, das flexibel genug ist, um alle Anforderungen zu erfüllen, deshalb verwenden Sie (ich) ORM nicht für komplexere Aufgaben.
– Jarek Tkaczyk
28. Mai 2014 um 21:30 Uhr
@emzero, ich habe vor kurzem angefangen, Laravel auszuprobieren, und ich stehe vor genau der gleichen Frage. Ich kann nicht glauben, wie ein Entwickler sagen kann, dass eine zusätzliche Abfrage in Ordnung ist, wo Join die Arbeit erledigen würde und / oder select * ist in Ordnung
– dav
5. Februar 2017 um 21:28 Uhr
Meine Vermutung ist, dass dies das eifrige Laden mehrerer Eins-zu-viele-Beziehungen ermöglicht. Sagen wir zum Beispiel, wir hatten auch einen Hundetisch:
class User extends Eloquent {
public function cats() {
return $this->hasMany('Cat');
}
public function dogs() {
return $this->hasMany('Dog');
}
}
Jetzt wollen wir beide eifrig mit dem User laden:
$users = User::with('cats','dogs')->get();
Es gibt keinen Join, der funktionieren würde, um diese in einer einzigen Abfrage zu kombinieren. Es funktioniert jedoch, eine separate Abfrage für jedes “with” -Element durchzuführen:
select * from `users`
select * from `cats` where `user`.`id` in ('1', '2', 'x')
select * from `dogs` where `user`.`id` in ('1', '2', 'x')
Obwohl diese Methode unter einigen einfachen Umständen eine zusätzliche Abfrage erzeugen kann, bietet sie die Möglichkeit, komplexere Daten eifrig zu laden, wenn die Join-Methode fehlschlägt.
Das ist meine Vermutung, warum das so ist.
Dies gilt für N<>M- und 1<>N-Beziehungen, aber nicht für 1<>1 oder N<>1. Selbst wenn Sie nur diese 2 implementieren, gibt es immer noch eine sehr große Verbesserung, insbesondere für große Sets. (Außerdem kann 1<>N auch mit einer zusätzlichen Abfrage gelöst werden, unabhängig von der Anzahl der Datensätze im Satz)
– Yaron U.
11. September 2016 um 18:45 Uhr
zum belongsTo Beziehungen gilt dies nicht
– dav
5. Februar 2017 um 21:13 Uhr
aber eloquent kann join für gehörtzu und eine andere Abfrage für hasMany verwenden
– fico7489
3. Dezember 2017 um 15:56 Uhr
There is no join that would work to combine these into a single query. Nun, es ist der linke Join
– Adam
18. September 2020 um 8:30 Uhr
Ich denke, der Join-Abfrageansatz hat einen fatalen Nachteil, wenn Sie LIMIT und/oder OFFSET verwenden möchten.
$users = User::with(‘cats’)->get() – dies gibt die folgenden 2 Abfragen aus.
select * from `users`
select * from `cats` where `user`.`id` in ('1', '2', 'x')
und es ist nicht eine einzige Abfrage wie
select * from users inner join cats on cats.user_id = users.id
aber nehmen wir an, wir müssen diesen Datensatz paginieren.
User::with(‘cats’)->paginate(10) – dies gibt die folgenden 2 Abfragen mit Limit aus.
select * from `users` limit 10
select * from `cats` where `user`.`id` in ('1', '2', 'x')
mit einem Join wird es wie sein
select * from users inner join cats on cats.user_id = users.id limit 10
Es werden 10 Datensätze abgerufen, dies bedeutet jedoch nicht 10 Benutzer, da jeder Benutzer mehrere Katzen haben kann.
Ein weiterer Grund meiner Meinung nach ist, dass eine Beziehung zwischen relationaler Datenbank und NOSQL-Datenbank mit dem getrennten Abfrageansatz einfach implementiert werden kann
Auch als vorherige Antwort ist id mehrdeutig, und Sie müssten jeder Anweisung den nicht gewünschten Tabellennamen voranstellen.
Auf der anderen Seite ist JOIN teurer als EXISTS und EXISTS ist schneller, weil es RDBMS nicht anweist, Daten abzurufen, prüfen Sie einfach, ob relevante Zeilen vorhanden sind. EXISTS wird verwendet, um einen booleschen Wert zurückzugeben, JOIN gibt eine ganz andere Tabelle zurück.
cats und users wahrscheinlich haben beide eine Spalte mit dem Namen id, wodurch Ihre vorgeschlagene Abfrage mehrdeutig bleibt. Das eifrige Laden von Laravel verwendet eine zusätzliche Abfrage, vermeidet jedoch dieses potenzielle Missgeschick.
Sie haben Recht. Es ist keine Möglichkeit, den Tabellennamen an zurückgegebene Spalten anzuhängen? Wie cat.id, cat.name, user.idetc?
– empz
28. Mai 2014 um 20:14 Uhr
Das ist mit Aliasen in der Abfrage trivial zu handhaben (z cat.id as cat__id), und ich würde erwarten, dass ein ORM das in Kauf nimmt.
– IMSoP
28. Mai 2014 um 20:15 Uhr
@IMSoP Genau, das würde ich auch erwarten.
– empz
28. Mai 2014 um 20:16 Uhr
@IMSoP Im Gegensatz zu einigen ORMs weiß Eloquent nicht, welche Spalten sich in den Tabellen befinden – meistens tut es dies SELECT *. Es würde nicht wissen, für welche Spalten ein Alias verwendet werden soll.
– ceejayoz
28. Mai 2014 um 20:28 Uhr
@ceejayoz Siehe meinen Kommentar oben bezüglich der Rückgabe einer zusätzlichen “Trennzeichen” -Spalte für jeden Join (ein Trick, den ich häufig beim Debuggen verwende). Es wäre sicherlich möglich, ein ORM zu erstellen, das Joins verwendet, ohne die Nichtschlüsselspalten im Voraus zu kennen. Dies ist entweder eine Designentscheidung von Eloquent oder einfach eine Einschränkung, an deren Behebung sie nicht interessiert sind.
– IMSoP
28. Mai 2014 um 20:44 Uhr
11576500cookie-checkWarum kann Laravel/Eloquent JOIN nicht für Eager Loading verwenden?yes
@deczo Ahmmm … weil es 1 Abfrage statt 2 ist? Das
user_id
anCat
ist nicht nullable, everyCat
gehört zu einemUser
… Also warum nichtinner join
?– empz
28. Mai 2014 um 20:26 Uhr
@deczo Auch dies ist kein großes Problem, wenn die ORM-Designer gesucht es anzugehen. Die Definition der Objekte könnte leicht zwischen „hat immer“ und „manchmal hat“ oder dem Abruf „auf jeden Fall mit“ und „möglicherweise mit“ unterscheiden und entsprechend innere, linke oder rechte Verknüpfung auswählen. Ich kenne Eloquent nicht, also haben sie vielleicht einfach beschlossen, die Dinge einfach zu halten.
– IMSoP
28. Mai 2014 um 21:09 Uhr
@deczo Ich glaube nicht, dass ich dir folge. Die Join-Klausel existiert aus einem bestimmten Grund, sodass Sie keine zusätzlichen Abfragen durchführen müssen, um Zeilen aus verschiedenen Tabellen abzugleichen. Wenn Sie zwischen verschiedenen Arten von Joins sprechen, wie left, right, inner… Ich denke, Sie könnten Eloquent sagen, welche je nach Beziehung im Modell verwendet werden soll.
belongsTo
würde bedeuten, dass es immer gehört, also kann der Fremdschlüssel nicht null sein. Sie könnten ein weiteres Like hinzufügencanBelongTo
Dies würde zulassen, dass der Fremdschlüssel null ist, und aus diesem Grund würde er die linke Verknüpfung verwenden. Weiß nicht, sag nur…– empz
28. Mai 2014 um 21:10 Uhr
Die Sache ist, dass ORM nur eine weitere Ebene ist, um das Leben einfacher zu machen. Sein Hauptzweck ist es, entwicklerfreundlich zu sein, und ich denke, das ist so einfach wie es nur sein kann. Es hat Fehler und Einschränkungen, es gibt Leistungsprobleme, auch Inkonsistenzen im Code, aber andererseits ist es wirklich eloquent und großartig für einfache Aufgaben. Allerdings gibt es kein ORM, das flexibel genug ist, um alle Anforderungen zu erfüllen, deshalb verwenden Sie (ich) ORM nicht für komplexere Aufgaben.
– Jarek Tkaczyk
28. Mai 2014 um 21:30 Uhr
@emzero, ich habe vor kurzem angefangen, Laravel auszuprobieren, und ich stehe vor genau der gleichen Frage. Ich kann nicht glauben, wie ein Entwickler sagen kann, dass eine zusätzliche Abfrage in Ordnung ist, wo Join die Arbeit erledigen würde und / oder
select *
ist in Ordnung– dav
5. Februar 2017 um 21:28 Uhr