Хорошо после нескольких часов исследований и до сих пор использующих DB:: select. Я должен задать этот вопрос. Потому что я собираюсь прогнать мой компьютер;).
Я хочу получить последний вход пользователя (база на отметке времени). Я могу сделать это с помощью raw 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
Я получил этот запрос из другого сообщения здесь в stackoverflow.
Я пробовал все, чтобы сделать это с помощью свободного конструктора запросов в Laravel, но без успеха.
Я знаю, что в руководстве говорится, что вы можете это сделать:
DB::table('users')
->join('contacts', function($join)
{
$join->on('users.id', '=', 'contacts.user_id')->orOn(...);
})
->get();
Но это не помогает, потому что я не вижу, как я могу использовать подзапрос там? Любой, кто может осветить мой день?
Хорошо для всех вас, прибывших сюда в отчаянии, ищущих ту же проблему. Я надеюсь, вы найдете это быстрее, чем я; O.
Вот как это решается. 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();
Я искал решение довольно связанной проблемы: поиск новейших записей на группу, который является специализацией типичного наибольшего n-на-группу с N = 1.
Решение включает в себя проблему, с которой вы здесь работаете (например, как построить запрос в Eloquent), поэтому я публикую его, так как он может быть полезен для других. Он демонстрирует более чистый способ создания подзапроса с использованием мощного интерфейса Eloquent, в котором имеется несколько столбцов соединения и 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();
ОБНОВИТЬ:
Начиная с Laravel 5.6.17 были добавлены объединения подзапросов, так что существует собственный способ создания запроса.
$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();
Я думаю, что вы ищете "joinSub". Поддерживается с laravel ^ 5.6. Если вы используете версию laravel ниже 5.6, вы также можете зарегистрировать ее как макрос в файле поставщика услуг приложения. как это 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.*']);
Запрос с дополнительным запросом в 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();
Вы можете использовать следующий аддон для обработки всех связанных с подзапросом функций из laravel 5. 5+
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();
Я не могу комментировать, потому что моя репутация недостаточно высока. @Franklin Rivero, если вы используете Laravel 5.2, вы можете установить привязки для основного запроса вместо объединения, используя метод setBindings.
Таким образом, основной запрос в ответе @ph4r05 будет выглядеть примерно так:
$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());
selectRaw
оператораselectRaw
! Вам посчастливилось передать в запрос свободный объект запроса вместо строки запроса?