2

Let's say I have a table like this:

id  user_id status  updateded_id
1   1       yes     2/5/2013
2   2       no      2/1/2013
3   3       yes     1/28/2013
4   2       yes     1/24/2013
5   2       no      1/20/2013
6   1       yes     1/16/2013
7   3       no      1/12/2013
8   4       yes     1/8/2013
9   5       yes     1/4/2013
10  5       no      12/31/2012
11  6       yes     12/27/2012
12  7       no      12/23/2012
13  6       yes     12/19/2012
14  4       yes     12/15/2012
15  3       no      12/11/2012
16  3       yes     12/7/2012
17  1       no      12/3/2012
18  1       yes     11/29/2012

I need to write a query that selects a user_id only if the status at the latest updateded_id is 'yes`.

For example user_id 1 will be choosen and user_id 2 won't. I write this in a complex Larvel query builder (Larvel version 4.2).

The result is multiple users. So in this query user is should be selected only once and only if the last occurrence in statuses table be yes.

I tried:

    $query->join('mytable', function ($join) use ($statuses) {
            ->whereRaw('mytable.status in ('.$statuses.') )
            ->orderBy('mytable.updated_at', 'desc')
            ->limit(1);
    });

AND:

    $query->join('statuses', function ($join) use ($statuses) {
            ->whereIn('mytable.status ,$statuses )
            ->orderBy('mytable.updated_at', 'desc')
            ->limit(1);
    });

Got error on whereIn and whereRaw function. Like they don'y exist.

So, Progress. Whit this query I can find one user that has the has 'yes' as the newest status. I can not find a way to make it choose all the user by this logic:

SELECT  s1.user_id 
FROM statuses s1
WHERE s1.user_id  = ( SELECT user_id  
                   FROM statuses s2
                   WHERE s2.status IN ('yes', 'no') ORDER BY s2.updateded_id DESC LIMIT 1) 
                   AND s1.status= 'yes';
forpas
  • 160,666
  • 10
  • 38
  • 76
matisa
  • 497
  • 3
  • 25

3 Answers3

2

You could do this.

A user has multiple statuses and you want to get the users based on the latest status right?

Eloquent user model

class User extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable;

    /*
     * Get the user's statuses
     */
    public function statuses()
    {
        return $this->hasMany(Status::class);
    }

    /**
     * Get the user's latest status.
     */
    public function latestStatus()
    {
        return $this->hasOne(Status::class)->latestOfMany('updated_at');
    }
}

Then you can write the query like this

return User::whereHas('latestStatus', function($query) {
    $query->where('status', 'yes');
})->get();
sithuaung
  • 434
  • 2
  • 6
2

I believe you're looking for what I call a "filter join." I'm not familiar with Laravel, so please accept MySQL:

SELECT t.user_id FROM table AS t
LEFT JOIN table AS filter ON (
  filter.user_id = t.user_id
  AND filter.updated_id > l.updated_id
)
WHERE filter.id IS NULL
AND t.status = 'yes';

Using the data you provided in the question, my output was:

1
3
4
5
6

I believe that satisfies the requirements of "users that have 'yes' as their newest status."

randy
  • 369
  • 4
  • 12
1

You can use NOT EXISTS in a MySql query, which (I hope) you can transform to Laravel code:

SELECT s1.user_id 
FROM statuses s1
WHERE s1.status = 'yes'
  AND NOT EXISTS (
            SELECT 1
            FROM statuses s2
            WHERE s2.user_id = s1.user_id AND s2.updateded_id > s1.updateded_id
          );

See the demo.

forpas
  • 160,666
  • 10
  • 38
  • 76