0

I have a table that has 100's of entries for over 1000 different products, each identified by a unique UID.

ID  UID                 MANY COLUMNS    CREATED AT
1   dqwdwnboofrzrqww1   ...             2018-02-11 23:00:43
2   dqwdwnboofrzrqww1   ...             2018-02-12 01:15:30

3   dqwdwnbsha5drutj5   ...             2018-02-11 23:00:44
4   dqwdwnbsha5drutj5   ...             2018-02-12 01:15:31

5   dqwdwnbvhfg601jk1   ...             2018-02-11 23:00:45
6   dqwdwnbvhfg601jk1   ...             2018-02-12 01:15:33

...

I want to be able to get the last entry for each UID.

ID  UID                 MANY COLUMNS    CREATED AT
2   dqwdwnboofrzrqww1   ...             2018-02-12 01:15:30
4   dqwdwnbsha5drutj5   ...             2018-02-12 01:15:317
6   dqwdwnbvhfg601jk1   ...             2018-02-12 01:15:33

Is this possible in one DB call?

I have tried using DB as well as Eloquent but so far I either get zero results or the entire contents of the Table.

Andy

M Khalid Junaid
  • 63,861
  • 10
  • 90
  • 118
Andy
  • 333
  • 4
  • 10
  • Is there a product table with a single row per product related to this table? That would make things easier to visualise and manage if you use eloquent – apokryfos Mar 11 '18 at 07:02
  • Not in this case no. Each row has a ton of updated info on each UID and I just need the last entry for each. – Andy Mar 11 '18 at 07:45

6 Answers6

3

This is easy enough to handle in MySQL:

SELECT t1.*
FROM yourTable t1
INNER JOIN
(
    SELECT UID, MAX(created_at) AS max_created_at
    FROM yourTable
    GROUP BY UID
) t2
    ON t1.UID        = t2.UID AND
       t1.created_at = t2.max_created_at;

Translating this over to Eloquent would be some work, but hopefully this gives you a good starting point.

Edit: You may want to use a LEFT JOIN if you expect that created_at could ever be NULL and that a given UID might only have null created values.

Tim Biegeleisen
  • 502,043
  • 27
  • 286
  • 360
  • @Andy Be sure that all latest `created_at` entries exist. – Tpojka Mar 11 '18 at 14:49
  • @Tpojka I don't think we need to do a `LEFT JOIN` here, because each `UID` is guaranteed to have at least one record coming from the subquery. Otherwise, it doesn't exist anyway. – Tim Biegeleisen Mar 11 '18 at 14:50
  • Off topic maybe, but `MAX(created_at)` wouldn't consider NULL values even if it is inserted later (by `id` which is later in time by it's nature)? Sorry for off topic, just trying to improve my knowledge and conclusion. – Tpojka Mar 11 '18 at 15:05
  • @Tpojka Good point, I edited my answer. Though I would wager that the OP would assume that every `UID` would be assigned a created date. – Tim Biegeleisen Mar 11 '18 at 15:18
1

You can use a self join to pick latest row for each UID

select t.*
from yourTable t
left join yourTable t1 on t.uid = t1.uid
and t.created_at < t1.created_at 
where t1.uid is null

Using laravel's query builder it would be similar to

DB::table('yourTable as t')
    ->select('t.*')
    ->leftJoin('yourTable as t1', function ($join) {
        $join->on('t.uid','=','t1.uid')
             ->where('t.created_at', '<', 't1.created_at');
    })
    ->whereNull('t1.uid')
    ->get();

Laravel Eloquent select all rows with max created_at

Laravel Eloquent group by most recent record

M Khalid Junaid
  • 63,861
  • 10
  • 90
  • 118
  • You certainly took me down the right road with your reply, thanks a lot. I will post what I did as I also had a Violation error I had to solve to do with GroupBy. – Andy Mar 11 '18 at 08:23
0

SELECT p1.* FROM product p1, product p2 where p1.CREATED_AT> p2.CREATED_AT group by p2.UID

0

You can achieve this with eloquent using orderBy() and groupBy():

$data = TblModel::orderBy('id','DESC')->groupBy('uid')->get();
Sohel0415
  • 9,523
  • 21
  • 30
0

SOLVED

Thanks to Tim and M Khalid for their replies. It took me down the right road but I hit a snag, hence why I am posting this solution.

This worked:

        $allRowsNeeded = DB::table("table as s")
            ->select('s.*')
            ->leftJoin("table as s1", function ($join) {
                $join->on('s.uid', '=', 's1.uid');
                $join->on('s.created_at', '<', 's1.created_at');
            })
            ->whereNull('s1.uid')
            ->get();

However I got an Access Violation so I had to go in to config/database.php and set

'strict' => false,

inside the 'mysql' config, which removes ONLY_FULL_GROUP_BY from the SQL_MODE.

Thanks again.

Andy
  • 333
  • 4
  • 10
-1

You have to use ORDER BY, and LIMITSQL parameters, which will lead you to an easy SQL request :

for exemple, in SQL you should have something like this :

SELECT *
FROM table_name
ORDER BY `created_at` desc
LIMIT 1

This will returns everything in the table. The results will be ordering by the column "created_at" descending. So the first result will be what you're looking for. Then the "LIMIT" tells to return only the first result, so you won't have all your database.

If you wanna make it with eloquent, here is the code doing the same thing :

$model = new Model;
$model->select('*')->orderBy('created_at')->first();
kevinniel
  • 1,088
  • 1
  • 6
  • 14
  • I want to return the last entry for ALL 1000+ products so I don't think your example does that. – Andy Mar 11 '18 at 07:07
  • I don't think it will. Each UID has many entries and there are over 1000 UID's. I only want to return the LATEST entry for EACH UID, so only one entry returned for each UID. Your example will just return the data sorted by created_at. – Andy Mar 11 '18 at 07:34
  • Oh ok ! Then just add a `->groupBy('UID')` and it'll make the job – kevinniel Mar 11 '18 at 07:55
  • Thanks for your reply but that causes an access violation. SQLSTATE[42000]: Syntax error or access violation: 1055 Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column . $modelClass->select('*')->orderBy('created_at')->groupBy('UID')->first(); I think the self join approach above might solve this but no luck so far :-) – Andy Mar 11 '18 at 08:05
  • then replace the `groupBy()`eloquent method with the `unique()` method. This should make the job. Here is the full code that should work : `$model->orderBy('created_at')->get()->unique('UID');` – kevinniel Mar 11 '18 at 08:09