2013-08-06 6 views
49

Хорошо после нескольких часов исследований и до сих пор использующих DB :: select Я должен задать этот вопрос. Потому что я собираюсь прогнать свой компьютер;).Laravel Fluent Query Builder Присоединиться к подзапросу

Я хочу получить последний вход пользователя (база на отметке времени). Я могу сделать это с сырым 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 

Я получил этот запрос от другого почтового here на StackOverflow.

Я пробовал все, чтобы сделать это с помощью свободного конструктора запросов в Laravel, но без успеха.

Я знаю, что руководство говорит, что вы можете сделать это:

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

Но это не помогает много, потому что я не понимаю, как я мог бы использовать подзапрос там? Любой, кто может осветить мой день?

ответ

105

ОК для всех вас, прибывших сюда в отчаянии, ищем ту же проблему. Надеюсь, ты найдешь это быстрее, чем я. О.

Так оно и решено. JoostK сказал мне в github, что «первым аргументом для объединения является таблица (или данные), к которой вы присоединяетесь». И он был прав.

Вот код. Разные таблицы и имена, но вы получите идею правильно? Это т

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

Этот пакет должен для создания набора инструментов PHP для создания запросов, но нам нужно записать запрос и ввести его в оператор 'selectRaw'! Удалось ли вам передать беглый объект запроса в запрос вместо строки запроса? – HPM

+0

Это становится невозможным, если у вас есть параметр в подзапросе - см., Например, ответ @ ph4r05, но я не уверен, можно ли его упростить, чтобы избежать этих уродливых toSql и addBinding – JustAMartin

6

Я искал решение для довольно проблемы, связанные: поиск новейших записей в группе, которая является специализацией типичного greatest-n-per-group с N = 1.

Решение включает в себя проблемы, которую вы (т. е. как построить запрос в «Красноречии»), поэтому я отправляю его, поскольку это может быть полезно для других. Он демонстрирует более чистый способ построения подзапроса с использованием мощного свободного интерфейса с ярко выраженным интерфейсом с несколькими столбцами соединения и where условие внутри объединенного подвыбора.

В моем примере я хочу получить новейшие результаты сканирования DNS (таблица scan_dns) в группе, идентифицированной watch_id. Я строю суб-запрос отдельно.

SQL, я хочу Eloquent генерировать:

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` 

Я сделал это следующим образом:

// 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

Запрос с подзапросом в 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(); 
Смежные вопросы