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

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

Related

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 5.5 select users based on session with roles as

I want to select all users of sessions where roles for the users are not admin or only roles where roles.name is user.
my user table is
session table is
and roles table is this
I am running this query and it is working fine but it selects all users including admin user. I only want to select user where roles.name is user
$sessions = Sessions::where('user_id', '<>', 'NULL')
->join('users', 'users.id', 'sessions.user_id')
->select('users.first_name','users.last_name',
DB::raw('count(sessions.user_id) as counts'))
->get();
You have first to join the roles with the users table, since there is no way with the set up you gave to know what is the role of a certain user, so after adding role_id as a foreign key in the users table you can make your query like this:
$sessions = Sessions::where('user_id', '<>', 'NULL')
->join('users', 'users.id', 'sessions.user_id')
->join('roles', 'roles.id', 'users.role_id')
->where('roles.name','user')
->select('roles.name as role','users.first_name','users.last_name',
DB::raw('count(sessions.user_id) as counts'))
->get();
You can use DB query for this..
DB::table(sessions)->where('roles', $user->hasRole('user'))->get();
:)

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

Codeigniter - convert mysql query to CodeIgniter's active record query

I'm trying to convert this SQL query into a codeigniter query
SELECT
uploads.EMAIL
FROM
uploads
JOIN (
SELECT EMAIL, COUNT(*) as num FROM uploads GROUP BY EMAIL
) c ON uploads.EMAIL = c.EMAIL
ORDER BY
c.num DESC,
EMAIL ASC
Thanks for the help
kind regards
I am not sure why you can't figure this out yourself using the active record documentation, but:
$this->db->select('uploads.EMAIL');
$this->db->from('uploads');
$this->db->join('(SELECT EMAIL, COUNT(*) as num FROM uploads GROUP BY EMAIL) c','uploads.EMAIL = c.EMAIL','',FALSE);
$this->db->order_by('c.num desc, uploads.EMAIL asc');
and then
$query = $this->db->get();
FYI, passing FALSE as the fourth parameter to the db->join() method will cause it not to escape the statement, so you should be careful if you're going to take external variables. This is, until CodeIgniter 3, the only way to do subqueries with active record without extending the active record class to add them.

CakePHP: How do I count the number of hasMany records in a find?

I have two models, Post hasMany Comment. How do I select all Post that have less than two Comment?
I tried using a find with 'fields'=>array('COUNT(Comment.id) as numComments','Post.*'), (and then doing a numComments < 2 in 'conditions'). But, I get a Unknown column 'Comment.id' in 'field list' error.
Thanks!
EDIT: I've gotten CakePHP to generate this query:
SELECT `Post`.*, FROM `posts` AS `Post`
LEFT JOIN comments AS `Comment` ON (`Post`.`id` = `Comment`.`text_request_id`)
WHERE COUNT(`Comment`.`id`) < 2
GROUP BY `Comment`.`post_id`
LIMIT 10
But I get an error #1111 - Invalid use of group function on the COUNT function.
EDIT:
Resolved, use the HAVING COUNT instead of WHERE COUNT.
class Post extends AppModel
{
var $name = "Post";
var $hasMany = array('Comment'=>array('counterCache'=>true));
}
add comment_count fields into posts
an that's all :-)
In raw SQL, the query would look something like this:
SELECT Post.*
FROM Post LEFT JOIN Comment ON Post.id = Comment.post_id
GROUP BY Comment.post_id
HAVING COUNT(Comment.id) < 2
Most of these are easily translated to Cake:
array(
'having' => array('COUNT(Comment.id) <' => 2),
'group' => array('Comment.post_id')
)
Cake does not automatically join hasMany tables though, this is something you'll need to do manually. Have a look at the documentation for the details.
Edit:
You can do a having clause as follows:
array(
'group' => 'Comment.post_id HAVING COUNT(Comment.id) < 2'
)
The limitations are string only for the group and cant be done without the group by. Cake 3 will probably include more SQL syntax such as HAVING
If anyone interested:
here's the doc about counterCache http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#belongsto
and here's more about adding multiple counterCache fields http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#multiple-countercache