Filter duplicates by field max value - mysql

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.

Related

How can I get total sum of every customers paid amount in Laravel ??

I'm trying to get highest paid amount from my laravel app. From my payments table I want to use customer_id & paid_amount for calculating hightest paid amount but my code isn't working expected! please see my code snippet below
N.B: I want to get all columns of payments table, but how I can do that?
$payments = Payment::with(['customer'])
->select(DB::raw('sum(paid_amount) as paid_amount, customer_id'))
->orderBy('customer_id', 'DESC')
->paginate(10);
Also I've tried this one but this times it shows me an error because I was using relations with Customer model!
$payments = Payment::select(DB::raw('sum(paid_amount) as paid_amount, customer_id'))
->groupBy('customer_id')
->paginate(10);
You can try the query this way to get SUM.
Payment::query()
->select(DB::raw("sum(paid_amount) as paid_amount, customer_id"))
->with(['customer'])
->groupBy('customer_id')
->orderBy('customer_id', 'DESC')
->get();
Here is the solution if you want to do it with pagination and relationship.
Payment::query()
->select(DB::raw("sum(paid_amount) as paid_amount, customer_id"))
->with(['customer'])
->groupBy('customer_id')
->orderBy('customer_id', 'DESC')
->paginate(10)
You can also try this
Payment::select(DB::raw("sum(payments.paid_amount) as paid_amount"), payments.customer_id")
->join('customers','payments.customer_id','=','customers.id')
->groupBy('payments.customer_id')
->orderBy('payments.customer_id', 'DESC')
->paginate(10);

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

How to get "unique" users by combining email and date

I am using Laravel 5.5.
I have a database which contains users. The problem is that some users exist more than one time because of a bug. I want to query my database and select all "unique" users.
By using the word "unique" I mean the below :
If a user with email "test#test.com" exists 50 times I want the row that created_at is closest to now.
My query, which returns all users is written below :
DB::table('users')
->select('name', 'surname', 'email', 'phone', 'answers', 'newsletter', 'created_at')
->get();
I got confused and I'm not sure if i should use limit combining it with order by created_at column.
Any ideas?
Ok, this is what you need to do: First off, you get a table with the users in their last created_at 'version'. Now you have a list of emails and dates. Then you perform a left join of all the users with that temporary table.
TL;DR:
$users = DB::select('select t1.* from users t1 right join (SELECT email, MAX(created_at) as created_at from users group by email) as t2 on t1.email=t2.email and t1.created_at=t2.created_at');
I hate raw SQL, and I hate subqueries, but this is the only way I know using generic SQL (I mean, you could do a better MySQL or MSSQL native queries, but this should do for you.)
You can use
DB::table('users')->select('name', 'surname', 'email','phone','answers','newsletter','created_at')->orderBy('created_at', 'desc')->groupBy('email')->get();
For more help refer Order By before Group By using Eloquent (Laravel)
What you need is groupby and orderby
try this code
DB::table('users')->select('name', 'surname', 'email','phone','answers','newsletter','created_at')
->orderBy('created_at', 'desc')
->groupBy('email')
->get();
hope it will help you if you need further info try above link!
To get latest user record among duplicates you can use a self join
DB::table('users as u')
->select('u.*')
->leftJoin('users as u1', function ($join) {
$join->on('u.email', '=', 'u1.email')
->whereRaw(DB::raw('u.created_at < u1.created_at'));
})
->whereNull('u1.id')
->get();
In plain SQL it would be something like
select u.*
from users u
left join users u1 on u.email = u1.email
and u.created_at < u1.created_at
where u1.id is null

How can I get all entities without a specific related entity?

I have these tables:
users
id
name
events
id
name
entries
id
user_id
event_id
How can I get all users that do not have an entry with event_id 4?
With this code:
$users = User::query();
$users->leftJoin('entries', 'users.id', '=', 'entries.user_id')
->where('event_id','!=', $event_id)->get();
I still get users that already have an entry for that specific event.
This is what I want:
Get all entries which have event_id 4
Get the user_id from those entries
Remove other entries which have that user_id.
$entries = Entry::where(event_id, '=', 4)->get();
foreach ($entries as &$entry) {
//remove all entries from $entries array which "user_id = $entry->user_id"
}
How can I do the above with just a query?
Going by your question following is the answer but i guess this is not what you finally want. so elaborate more and specify the sample input and output datasets
select * from your_table where event_id <> 4;
The SQL you want is:
select user_id
from t
group by user_id
having sum(event_id = 4) = 0;
select u.*
from User u
left join entries e
on e.user_id = u.id and e.event_id = 4
where e.id is null
You're looking for <>, not !=. Also you're not letting it know which table event_id is on.
Try
$users = User::query();
$users->leftJoin('entries', 'users.id', '=', 'entries.user_id')
->where('entries.event_id','<>', $event_id)->get();
More information can be found at https://laravel.com/docs/5.4/queries#where-clauses
You can use the whereDoesntHave builder method.
$users = User::query()->whereDoesntHave("entry", function($q) use ($event_id) {
$q->where('id', $event_id);
})->get();
The method takes a closure in which you can define criteria for the related entities that must not exist.
If you need the users and the entries, I think you can still join the entries using with.
$users = User::query()
->with('entries')
->whereDoesntHave("entry", function($q) use ($event_id) {
$q->where('id', $event_id); })
->get();

Laravel. Using WHERENOTIN

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