2016-10-06 3 views
1

Ich versuche, eine linke Verknüpfung zu dieser Abfrage hinzuzufügen, die ich verwendet habe, um nPerGroup von verwandten Datensätzen zu erhalten. Ich habe die Abfrage in SQL erstellt, weiß aber nicht, wie ich sie in Laravel-Abfrage-Builder-Code konvertieren soll.Hinzufügen einer linken Verknüpfung in Laravel Query/Unterabfrage-Generator

Der Grund, warum ich einen left join hinzufügen möchte, ist für Leistungszwecke. Wenn ich alle Spalten von Anfang an abrufe, dauert die Abfrage zu lange (ca. 6 Sek. Auf 400k Zeilen), während bei einer left join nur eine halbe Sekunde geschnitten wird.

Ich habe versucht, eine left join nach der mergeBindings, aber ich konnte nicht herausfinden, wie die Spalten, die ich brauche in der ersten Auswahl angeben. Egal, was ich versuchte, die erste Wahl blieb immer als select *.

Hier ist der Laravel Umfang Code, den ich ändern müssen:

public function scopeNPerGroup($query, $group, $n = 10, $columns) 
{ 

    // queried table 
    $table = ($this->getTable()); 

    // initialize MySQL variables inline 
    $query->from(DB::raw("(SELECT @rank:=0, @group:=0) as vars, {$table}")); 

    // if no columns already selected, let's select * 
    if (! $query->getQuery()->columns && empty($columns)) { 
     $query->select("{$table}.*"); 
    } 
    elseif (!empty($columns)) { 
     foreach ($columns as $column) { 
      $query->addSelect($column); 
     } 
    } 

    // make sure column aliases are unique 
    $groupAlias = 'group_'.md5(time()); 
    $rankAlias = 'rank_'.md5(time()); 

    // apply mysql variables 
    $query->addSelect(DB::raw(
     "@rank := IF(@group = {$group}, @rank+1, 1) as {$rankAlias}, @group := {$group} as {$groupAlias}" 
    )); 

    // make sure first order clause is the group order 
    $query->getQuery()->orders = (array) $query->getQuery()->orders; 
    array_unshift($query->getQuery()->orders, ['column' => $group, 'direction' => 'asc']); 

    // prepare subquery 
    $subQuery = $query->toSql(); 

    // prepare new main base Query\Builder 
    $newBase = $this->newQuery() 
     ->from(DB::raw("({$subQuery}) as {$table}")) 
     ->mergeBindings($query->getQuery()) 
     ->where($rankAlias, '<=', $n) 
     ->getQuery(); 

    // replace underlying builder to get rid of previous clauses 
    $query->setQuery($newBase); 
} 

Hier ist die SQL, die oben durch den Code produziert wird:

SELECT * 
FROM (SELECT `positions`.`id`, 
       `positions`.`keyword_id`, 
       `positions`.`position`, 
       @rank := IF(@group = keyword_id, @rank + 1, 1) AS 
       rank_fa9d7a6f55c38becc0b28f348651a856, 
       @group := keyword_id       AS 
         group_fa9d7a6f55c38becc0b28f348651a856 
     FROM (SELECT @rank := 0, 
         @group := 0) AS vars, 
       positions 
     ORDER BY `keyword_id` ASC, 
        `created_at` DESC) AS positions 
WHERE `rank_fa9d7a6f55c38becc0b28f348651a856` <= '2' 
     AND `positions`.`keyword_id` IN ('1', '2', ...) 

Und hier ist die SQL ich es brauche, anstatt zu produzieren: (Könnte anders sein, solange es die gleiche Sache erreicht, die die zusätzliche Spalte holt.)

SELECT `positionsA`.`id`, `positionsA`.`keyword_id`, `positionsA`.`position`, `positions`.`url` 
FROM (SELECT `positions`.`id`, 
       `positions`.`keyword_id`, 
       `positions`.`position`, 
       @rank := IF(@group = keyword_id, @rank + 1, 1) AS 
       rank_e2d9373d3bb35d6aabe9ffc57ff29c1c, 
       @group := keyword_id       AS 
         group_e2d9373d3bb35d6aabe9ffc57ff29c1c 
     FROM (SELECT @rank := 0, 
         @group := 0) AS vars, 
       positions 
     ORDER BY `keyword_id` ASC, 
        `created_at` DESC) AS positionsA 
LEFT JOIN `positions` on `positionsA`.`id` = `positions`.`id` 
WHERE `rank_e2d9373d3bb35d6aabe9ffc57ff29c1c` <= '2' 
     AND `positionsA`.`keyword_id` IN ('1', '2', ...) 

Antwort

0

Ich glaube, Sie so etwas wie dieses neeed:

$newBase = $this->newQuery() 
    ->from(DB::raw("({$subQuery}) as {$table}A")) 
    ->leftJoin('positions', 'positionsA.id', '=', 'positions.id') 
    ->mergeBindings($query->getQuery()) 
    ->where($rankAlias, '<=', $n) 
    ->getQuery(); 
+0

Yep ich, dass viel zu bekommen hatte, aber ich kann nicht herausfinden, wie die spezifischen Felder in der ersten 'SELECT' Anweisung auszuwählen. Es läuft immer für 'SELECT *' Das funktioniert auch, aber es dauert etwa 2.5s zu laufen, während meine rohe Abfrage in etwa einer halben Sekunde mit vorgewählten Spalten läuft. Das ist irgendwie, warum ich es so nah wie möglich an die rohe Frage heranholen möchte. – zen

+0

Eigentlich ist das nicht der Grund dafür, dass es langsamer läuft. Der Grund ist eigentlich, ich muss herausfinden, wie man die 'positions.keyword_id' in' positionsA.keyword_id' ändert. Das ist der Schuldige. Ich denke jedoch, dass ein Teil vom übergeordneten Modell generiert wird, in dem die Beziehung abgerufen wird. Irgendeine Idee, wie man das ändert? – zen

Verwandte Themen