2013-08-06 18 views
49

Okay nach Stunden der Forschung und immer noch mit DB :: select Ich muss diese Frage stellen. Weil ich gerade meinen Computer weglege;).Laravel Fluent Query Builder Join mit Unterabfrage

Ich möchte die letzte Eingabe eines Benutzers erhalten (basierend auf dem Zeitstempel). Ich kann dies tun mit dem rohen SQL

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 diese Abfrage aus einem anderen Beitrag here auf Stackoverflow bekam.

Ich habe alles versucht, dies mit dem fließenden Abfrage-Generator in Laravel jedoch ohne Erfolg zu tun.

Ich weiß, das Handbuch sagt, dies zu tun:

DB::table('users') 
    ->join('contacts', function($join) 
    { 
     $join->on('users.id', '=', 'contacts.user_id')->orOn(...); 
    }) 
    ->get(); 

Aber das hilft nicht viel, weil ich sehe nicht, wie ich da eine Unterabfrage verwenden könnte? Wer kann meinen Tag erhellen?

Antwort

105

Ok für alle da draußen, die hier in Verzweiflung auf der Suche nach dem gleichen Problem angekommen. Ich hoffe, du wirst das schneller finden als ich, O.

So wird es gelöst. JoostK hat mir bei GitHub gesagt, dass "das erste Argument für den Beitritt die Tabelle (oder die Daten) ist, der Sie beitreten." Und er hatte Recht.

Hier ist der Code. Unterschiedliche Tabelle und Namen, aber Sie werden die Idee richtig bekommen? Es t

DB::table('users') 
     ->select('first_name', 'TotalCatches.*') 

     ->join(DB::raw('(SELECT user_id, COUNT(user_id) TotalCatch, 
       DATEDIFF(NOW(), MIN(created_at)) Days, 
       COUNT(user_id)/DATEDIFF(NOW(), MIN(created_at)) 
       CatchesPerDay FROM `catch-text` GROUP BY user_id) 
       TotalCatches'), 
     function($join) 
     { 
      $join->on('users.id', '=', 'TotalCatches.user_id'); 
     }) 
     ->orderBy('TotalCatches.CatchesPerDay', 'DESC') 
     ->get(); 
+3

Dieses Paket soll um einen PHP-Werkzeugsatz zum Erstellen von Abfragen bereitzustellen, müssen wir jedoch die Abfrage aufschreiben und sie mit 'selectRaw'-Anweisung injizieren! Hatten Sie ein Glück, ein fließendes Abfrage-Objekt anstelle der Abfrage-Zeichenfolge an die Abfrage zu übergeben? – HPM

+0

Dies wird unmöglich, wenn Sie einen Parameter im subselect haben - siehe @ ph4r05 Antwort, aber ich bin mir nicht sicher, ob es vereinfacht werden könnte, um diese hässlichen toSql und addBinding zu vermeiden – JustAMartin

6

Ich war nach einer Lösung für eine ganze verwandtes Problem suchen: die neuesten Datensätze pro Gruppe zu finden, die eine Spezialisierung eines typischen greatest-n-per-group mit N = 1.

Die Lösung ist mit dem Problem behaftet Sie beschäftigen sich hier (dh, wie man die Abfrage in Eloquent erstellt), also poste ich es, wie es für andere hilfreich sein könnte. Es demonstriert eine sauberere Art der Sub-Abfrage-Konstruktion mit leistungsstarken Eloquent fließenden Schnittstelle mit mehreren Join-Spalten und where Bedingung innerhalb verbundenen Sub-Select.

In meinem Beispiel möchte ich die neuesten DNS-Scan-Ergebnisse (Tabelle scan_dns) pro Gruppe abrufen, die durch watch_id identifiziert werden. Ich erstelle die Unterabfrage separat.

Die SQL I Eloquent möchte generieren:

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 auf folgende Weise:

// 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(); 
0

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(); 
Verwandte Themen