Okay, nach stundenlanger Recherche und immer noch mit DB :: select muss ich diese Frage stellen. Weil ich gerade dabei bin, meinen Computer wegzuwerfen;).
Ich möchte die letzte Eingabe eines Benutzers erhalten (basierend auf dem Zeitstempel). Ich kann das mit dem rohen SQL machen
SELECT c.*, p.*
FROM users c INNER JOIN
(
SELECT user_id,
MAX(created_at) MaxDate
FROM `catch-text`
GROUP BY user_id
) MaxDates ON c.id = MaxDates.user_id INNER JOIN
`catch-text` p ON MaxDates.user_id = p.user_id
AND MaxDates.MaxDate = p.created_at
Ich habe diese Abfrage von einem anderen Beitrag hier auf stackoverflow erhalten.
Ich habe alles versucht, um dies mit dem fließenden Abfrage-Builder in Laravel zu tun, jedoch ohne Erfolg.
Ich weiß, dass das Handbuch besagt, dass Sie dies tun können:
DB::table('users')
->join('contacts', function($join)
{
$join->on('users.id', '=', 'contacts.user_id')->orOn(...);
})
->get();
Aber das hilft nicht viel, weil ich nicht sehe, wie ich dort eine Unterabfrage verwenden könnte? Wer kann meinen Tag erhellen?
selectRaw
Anweisung einfügen ! Hatten Sie Glück, ein fließendes Abfrageobjekt anstelle einer Abfragezeichenfolge an die Abfrage zu übergeben?Ich suchte nach einer Lösung für ein ziemlich verwandtes Problem: die neuesten Datensätze pro Gruppe zu finden, was eine Spezialisierung eines typischen größten n pro Gruppe mit N = 1 ist.
Die Lösung betrifft das Problem, mit dem Sie sich hier befassen (dh wie die Abfrage in Eloquent erstellt wird), sodass ich sie veröffentliche, da sie für andere hilfreich sein könnte. Es zeigt eine sauberere Art der Erstellung von Unterabfragen unter Verwendung einer leistungsstarken fließenden Eloquent-Schnittstelle mit mehreren Verknüpfungsspalten und
where
Bedingungen innerhalb der verknüpften Unterauswahl.In meinem Beispiel möchte ich die neuesten DNS-Scanergebnisse (Tabelle
scan_dns
) pro Gruppe abrufen, die durch gekennzeichnet sindwatch_id
. Ich erstelle die Unterabfrage separat.Das SQL, das Eloquent generieren soll:
SELECT * FROM `scan_dns` AS `s` INNER JOIN ( SELECT x.watch_id, MAX(x.last_scan_at) as last_scan FROM `scan_dns` AS `x` WHERE `x`.`watch_id` IN (1,2,3,4,5,42) GROUP BY `x`.`watch_id`) AS ss ON `s`.`watch_id` = `ss`.`watch_id` AND `s`.`last_scan_at` = `ss`.`last_scan`
Ich habe es folgendermaßen gemacht:
// table name of the model $dnsTable = (new DnsResult())->getTable(); // groups to select in sub-query $ids = collect([1,2,3,4,5,42]); // sub-select to be joined on $subq = DnsResult::query() ->select('x.watch_id') ->selectRaw('MAX(x.last_scan_at) as last_scan') ->from($dnsTable . ' AS x') ->whereIn('x.watch_id', $ids) ->groupBy('x.watch_id'); $qqSql = $subq->toSql(); // compiles to SQL // the main query $q = DnsResult::query() ->from($dnsTable . ' AS s') ->join( DB::raw('(' . $qqSql. ') AS ss'), function(JoinClause $join) use ($subq) { $join->on('s.watch_id', '=', 'ss.watch_id') ->on('s.last_scan_at', '=', 'ss.last_scan') ->addBinding($subq->getBindings()); // bindings for sub-query WHERE added }); $results = $q->get();
AKTUALISIEREN:
Seit Laravel 5.6.17 wurden die Unterabfrage -Joins hinzugefügt, sodass es eine native Möglichkeit gibt, die Abfrage zu erstellen.
$latestPosts = DB::table('posts') ->select('user_id', DB::raw('MAX(created_at) as last_post_created_at')) ->where('is_published', true) ->groupBy('user_id'); $users = DB::table('users') ->joinSub($latestPosts, 'latest_posts', function ($join) { $join->on('users.id', '=', 'latest_posts.user_id'); })->get();
quelle
$userInfoSubquery = DB::table('user_info') ->select() ->orderBy('info_date', 'DESC') ->limit(1);
meine Unterabfrage. Aber wenn die neuesten Informationen nicht dem Benutzer gehören, werden sie nichts zurückgeben ... Also muss ich in der Lage sein, etwas zu tun wie:->where('user_id', '=', userid)
irgendwie in der Unterabfrage ...Ich denke, was Sie suchen, ist "joinSub". Es wird von Laravel ^ 5.6 unterstützt. Wenn Sie eine Laravel-Version unter 5.6 verwenden, können Sie diese auch als Makro in Ihrer App Service Provider-Datei registrieren. wie folgt https://github.com/teamtnt/laravel-scout-tntsearch-driver/issues/171#issuecomment-413062522
$subquery = DB::table('catch-text') ->select(DB::raw("user_id,MAX(created_at) as MaxDate")) ->groupBy('user_id'); $query = User::joinSub($subquery,'MaxDates',function($join){ $join->on('users.id','=','MaxDates.user_id'); })->select(['users.*','MaxDates.*']);
quelle
Abfrage mit Unterabfrage in Laravel
$resortData = DB::table('resort') ->leftJoin('country', 'resort.country', '=', 'country.id') ->leftJoin('states', 'resort.state', '=', 'states.id') ->leftJoin('city', 'resort.city', '=', 'city.id') ->select('resort.*', 'country.name as country_name', 'states.name as state_name','city.name as city_name', DB::raw("(SELECT GROUP_CONCAT(amenities.name) from resort_amenities LEFT JOIN amenities on amenities.id= resort_amenities.amenities_id WHERE resort_amenities.resort_id=resort.id) as amenities_name"))->groupBy('resort.id') ->orderBy('resort.id', 'DESC') ->get();
quelle
Ich kann nichts dazu sagen, weil mein Ruf nicht hoch genug ist. @Franklin Rivero Wenn Sie Laravel 5.2 verwenden, können Sie die Bindungen für die Hauptabfrage anstelle des Joins mithilfe der setBindings-Methode festlegen.
Die Hauptabfrage in der Antwort von @ ph4r05 würde also ungefähr so aussehen:
$q = DnsResult::query() ->from($dnsTable . ' AS s') ->join( DB::raw('(' . $qqSql. ') AS ss'), function(JoinClause $join) { $join->on('s.watch_id', '=', 'ss.watch_id') ->on('s.last_scan_at', '=', 'ss.last_scan'); }) ->setBindings($subq->getBindings());
quelle
Sie können das folgende Addon verwenden, um alle Funktionen für Unterabfragen ab Laravel 5.5+ zu verwalten
https://github.com/maksimru/eloquent-subquery-magic
User::selectRaw('user_id,comments_by_user.total_count')->leftJoinSubquery( //subquery Comment::selectRaw('user_id,count(*) total_count') ->groupBy('user_id'), //alias 'comments_by_user', //closure for "on" statement function ($join) { $join->on('users.id', '=', 'comments_by_user.user_id'); } )->get();
quelle
Ich bin auf Laravel 7.25 und ich weiß nicht, ob es in früheren Versionen unterstützt wird oder nicht, aber es ist ziemlich gut.
Syntax für die Funktion:
Beispiel:public function joinSub($query, $as, $first, $operator = null, $second = null, $type = 'inner', $where = false)
Anzeigen / Abrufen der Benutzer-ID und der Gesamtzahl der von ihnen verbleibenden Beiträge, die zwei Tabellenbenutzer und Beiträge verbinden.
return DB::table('users') ->joinSub('select user_id,count(id) noOfPosts from posts group by user_id', 'totalPosts', 'users.id', '=', 'totalPosts.user_id', 'left') ->select('users.name', 'totalPosts.noOfPosts') ->get();
Alternative:
Wenn Sie 'left' für leftjoin nicht erwähnen möchten, können Sie eine andere vorgefertigte Funktion verwenden
public function leftJoinSub($query, $as, $first, $operator = null, $second = null) { return $this->joinSub($query, $as, $first, $operator, $second, 'left'); }
Und ja, es ruft tatsächlich dieselbe Funktion auf, aber es übergibt den Join-Typ selbst. Sie können dieselbe Logik für andere Joins anwenden, z. B. righJoinSub (...) usw.
quelle