Laravel. Using WHERENOTIN - mysql

Basically I want to build tihs query in Laravel, but it does not work.
SELECT films.id, films.name AS film
FROM films
WHERE films.id NOT IN
(
SELECT films.id
FROM actors, actor_film, films
WHERE actors.id = actor_film.actor_id
AND actor_film.film_id = films.id
GROUP BY films.id
)
ORDER BY films.id DESC
LIMIT 600
;
Using a "whereNotIn" I have written these two queries:
The first one get all films in the Data Base that has at least an actor associated like this:
$films_with_actors = DB::table('films')
->join('actor_film', 'actor_film.film_id', '=', 'films.id')
->join('actors', 'actors.id', '=', 'actor_film.actor_id')
->select( 'films.id')
->groupBy('films.id')
->get();
Now I want to get the films that do not have associated an actor. For that I am trying to get the ID that are not included in the previous method, like this:
$films_with_no_actors = DB::table('films')
->whereNotIn('films.id', $films_with_actors)
->orderBy('films.id', 'desc')
->take(500)
->get();
-
Any help?

I am giving you a basic solution based on the code you shared.
In laravel you have a method called pluck for retrieving an array with all the values for a given key.
Therefore, you can get only the ids for the $films_with_actors. Something like (based on your first query):
$films_with_actors = DB::table('films')
->join('actor_film', 'actor_film.film_id', '=', 'films.id')
->join('actors', 'actors.id', '=', 'actor_film.actor_id')
->select( 'films.id')
->groupBy('films.id')
->pluck('id')->toArray();
Now you have an array with the ids and you can include that array in the whereNotIn clause of your second query:
$films_with_no_actors = DB::table('films')
->whereNotIn('films.id', $films_with_actors)
->orderBy('films.id', 'desc')
->take(500)
->get();

Related

Laravel select unique products with lowest price from one table

Good day, I'm trying to get unique products with lowest prices.
I'm having a products table like this:
I would like to get a list of products with all columns. Now there are some products that have more than one supplier in that case I want to grab the product with the lowest cost_price.
So far I have tried this
$products = DB::table('products')
->select('identifier')
->selectRaw('MIN(cost_price) as cost_price')
->where('stock', '>', 0)
->groupBy('identifier')
->orderBy('cost_price', 'asc')
->distinct()->get();
This query returns me the correct results but I cant add more columns every time I add a column for example stock in select I need to add as well in GroupBy and then I'm just getting all products.
How to do it?
Thank you for reading.
You need greatest-n-per-group solution/approach for this problem.
The query;
SELECT products.*
FROM products
INNER JOIN (SELECT identifier, MIN(cost_price) AS minPrice
FROM products
WHERE stock > 0
GROUP BY identifier) AS sub
ON sub.minPrice = products.cost_price and sub.identifier = products.identifier;
The query builder version;
$sub = DB::table('products')
->where('stock', '>', DB::raw(0))
->groupBy('identifier')
->select('identifier', DB::raw('min(cost_price) as minPrice'));
return DB::table('products')
->join(DB::raw('(' . $sub->toSql() . ') as sub'), function ($join) {
$join->on('sub.minPrice', '=', 'products.cost_price');
$join->on('sub.identifier', '=', 'products.identifier');
})
->get(['products.*']);

Mysql query gives me same count from both tables

I'm getting users and counting their likes and their items on other tables. The problem is every time likes_count equals created_count. If I removed the second join of the items. It just works fine and get's the right count for the likes. Any help please. I'm using laravel.
$users = User::select(DB::raw('users.id, users.name,
users.email,users.image,
COUNT(likes.id) AS likes_count,
COUNT(items.id) AS created_count'))
->leftJoin('likes', 'users.id', '=', 'likes.user_id')
->leftJoin('items', 'users.id', '=', 'items.user_id')
->get();
When using Laravel, They discourage to write raw database queries. So Always try to stick with laravel.
$users = User::select(['id', 'name', 'email', 'image'])
->withCount('likes')
->withCount('items')
->get();
It's that simple.
NOTE
Note that you need to have defined those models and relationships.
I just solved the issue. By adding distinct inside count. Thanks to # CHARITRASHRESTHA. I found that answer in the link he provided.
$users = User::select(DB::raw('users.id, users.name,
users.email,users.image,
COUNT(distinct likes.id) AS likes_count,
COUNT(distinct items.id) AS created_count'))
->leftJoin('likes', 'users.id', '=', 'likes.user_id')
->leftJoin('items', 'users.id', '=', 'items.user_id')
->get();
Please add below code into user model.
public function likes(){
return $this->hasMany('App\Model\Likes','user_id','id');
}
public function items(){
return $this->hasMany('App\Model\Items','user_id','id');
}
and use below query to get count:
$users = User::select(['id', 'name', 'email', 'image'])->withCount('likes','items')->get();

Filter duplicates by field max value

I have a problem with getting data. I'm trying to get all users with there max value (points). My question is just like OData filter by max value and field.
Code for my question is:
$query = User::select('users.id','users.username','games.points',
'games.created_at')
->join('games', 'users.id', '=', 'games.user_id')
->whereBetween('games.created_at', [$start, $end]);
But the problem is when I want to remove duplicates and get max points for each user.
In the version where I don't need whereBetween I'm doing in this way:
return User::select('users.id', 'users.username', 'games.points',
'games.created_at')
->join('games', 'users.id', '=', 'games.user_id')
->whereRaw('(users.id, games.points) IN (SELECT user_id, MAX(points)
FROM games GROUP BY user_id)')
->orderByDesc('points')
->get();
Thanks
Why don't you simply GROUP BY your query on users.id and users.username?
-- I let you translate the query into your framework
SELECT users.id, users.username, max(games.points) as points
FROM users, games
WHERE users.id = games.user_id
AND games.created_at BETWEEN $start AND $end
GROUP BY users.id, users.username
ORDER BY max(games.points) DESC
That way, you'll get the max of points per user (id and username).
Hope this helps.
I have resolved my problem with using unique method form eloquent.
Game::with('user')
->whereBetween('created_at', [$start, $end])
->orderByDesc('points')
->orderBy('created_at')
->limit($limit)
->get()
->unique('user_id')
->values();
Thanks a lot to Lucas for his time and his help.

Laravel DB query builder left join with related counts

I am new in Laravel and facing an issue with the DB query builder.
I want to fetch all the records from users table and want to count related wishlists in wishlists table. Here is my query.
select `users`.*, count(wishlists.id) as wishlists_count from `users` left join `wishlists` on `users`.`id` = `wishlists`.`uid` where `users`.`status` = 1 group by `wishlists`.`id`
I want to do it with laravel query builder. Here is what I am trying.
$leads = DB::table('users')
->selectRaw('users.*', 'count(wishlists.id) as wishlists_count')
->leftJoin('wishlists', 'users.id', '=', 'wishlists.uid')
->where('users.status',1)
->groupBy('wishlists.id')
->get();
It is showing this error.
Argument 2 passed to Illuminate\Database\Query\Builder::selectRaw()
must be of the type array, string given.
Any help will be greatly appreciated.
Thanks in Advance.
This should work. From the documentation, the second argument to selectRaw() is an optional array of bindings.
However, you can achieve what you are looking for with a combination of select() and DB::raw()
$leads = DB::table('users')
->select('users.*', DB::raw('count(wishlists.id) as wishlists_count'))
->leftJoin('wishlists', 'users.id', '=', 'wishlists.uid')
->where('users.status',1)
->groupBy('wishlists.uid')
->get();
Edit: The above answer is wrong. I would consider the following alternatives instead.
Either you could achieve it with a Sub-Query with something like the following:
$wishlist = Wishlist::select(DB::raw('count(wishlists.id)'))
->whereColumn('uid', 'users.id')
->getQuery();
$users = User::select('users.*')
->selectSub($wishlist, 'wishlists_count')
->get();
Or with a much easier way (assuming you have a wishlists relationship set up)
Users::withCount('wishlists')->get();
foreach($users as $user) {
echo $user->wishlists_count;
}

How to select count with Laravel's fluent query builder?

Here is my query using fluent query builder.
$query = DB::table('category_issue')
->select('issues.*')
->where('category_id', '=', 1)
->join('issues', 'category_issue.issue_id', '=', 'issues.id')
->left_join('issue_subscriptions', 'issues.id', '=', 'issue_subscriptions.issue_id')
->group_by('issues.id')
->order_by(DB::raw('COUNT(issue_subscriptions.issue_id)'), 'desc')
->get();
As you can see, I am ordering by a count from the joined table. This is working fine. However, I want this count returned with my selections.
Here is the my raw sequel query that works fine.
Select issues.*, COUNT(issue_subscriptions.issue_id) AS followers
FROM category_issue JOIN Issues ON category_issue.issue_id = issues.id
LEFT JOIN issue_subscriptions ON issues.id = issue_subscriptions.issue_id
WHERE category_issue.category_id = 1
GROUP BY issues.id
ORDER BY followers DESC
How would I go about this select using Laravel's fluent query builder? I am aware I can use a raw sql query but I would like to avoid that if possible.
You can use an array in the select() to define more columns and you can use the DB::raw() there with aliasing it to followers. Should look like this:
$query = DB::table('category_issue')
->select(array('issues.*', DB::raw('COUNT(issue_subscriptions.issue_id) as followers')))
->where('category_id', '=', 1)
->join('issues', 'category_issue.issue_id', '=', 'issues.id')
->left_join('issue_subscriptions', 'issues.id', '=', 'issue_subscriptions.issue_id')
->group_by('issues.id')
->order_by('followers', 'desc')
->get();
$count = DB::table('category_issue')->count();
will give you the number of items.
For more detailed information check Fluent Query Builder section in beautiful Laravel Documentation.