Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

LaraBABA's avatar

Completely stuck with GroupBy and orderBy

Hi all,

I would like to build my query in simple form before jumping with Eloquent.

I am really stuck on this query:

SELECT DISTINCT
  chats.title,
  chats.object,
  MAX(chats.created_at) AS latest,
  SUM(IF(chats.admin_read = 0, 1, 0)) AS unread,
  chats.id,
  chats.group_id,
  chats.message
FROM chats
WHERE chats.group_id = 82
GROUP BY chats.object
ORDER BY latest

I am trying to get the results by grouping all the chats.object together while showing the latest chats.message per group of chats.object. eg:

chats.object | chats.message object1 | latest message object2 | latest message object3 | latest message

I was really expecting the "MAX(chats.created_at) AS latest" to reorder internally the messages by the latest ones in the groupBy but I keep seeing the oldest messages per groupBy Instead.

I cannot work it out, any idea what I am doing wrong please?

Thank you so much.

0 likes
58 replies
vincent15000's avatar

You don't show the structure of your database. Do several chats have the same object ? What is an object in your database ?

LaraBABA's avatar

@vincent15000

Sorry, this is my structure:

CREATE TABLE IF NOT EXISTS `chats` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `user_id` bigint(20) unsigned NOT NULL,
  `group_id` bigint(20) unsigned NOT NULL,
  `admin_read` tinyint(1) NOT NULL DEFAULT 0,
  `chat_object` varchar(15) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `chat_title` varchar(150) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `message` text COLLATE utf8mb4_unicode_ci NOT NULL,

  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3488 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
sr57's avatar

@user476820

I cannot work it out, any idea what I am doing wrong please?

Sure you are working on mysql

Mysql, by default, uses a non strict mode, that does not conform with sql standard ... which says you have to use aggregate function to all attributes not in the group by of the select.

1 like
LaraBABA's avatar

@sr57 Thanks for the reply. I was trying to build my query with DBForge query builder(for mysql), and while selecting all the columns and adding the clauses, this is the code that it gave me.

Question for you, I was always told in this forum to use strict mode with Laravel, is this correct?

LaraBABA's avatar

@sr57 Not sure I understood, I said I was set on strict mode, this is correct no?

sr57's avatar

@vincent15000

thought MySQL was in strict mode by default.

It can depend of your provider, but if you install it (on debian) it should be on "non strict mode"

1 like
sr57's avatar

@User476820

I was always told in this forum to use strict mode with Laravel, is this correct?

With or without Laravel, don't use "non strict mode" except if you know what you do ...

1 like
sr57's avatar

@user476820

I am set on strict mode right now

Good

2 possibilities

1 - you are able to use a aggregrate function for all fleds not in the group by : simple query

2 - if not, write you query with the aggregate you need and join to a second select on all the fields from the first one.

PS : same solution with postgresql that conforms better to sql standard.

LaraBABA's avatar

@sr57 Let me try with the second select, not an easy one I have to admit as I tried earlier on but the results was a double SUM of admin_read

sr57's avatar

@user476820 @vincent15000

When I wrote

sql standard ... which says you have to use aggregate function to all attributes not in the group by of the select.

It's in fact pure logic, without aggregate function what should be the return of a field that is not grouped?

LaraBABA's avatar

I tried this:

DB::select("SELECT
  C1.chat_title,
  C1.chat_object,
  C1.created_at,
  C1.id,
  MAX(C1.message) AS messages,
  C2.unread,
  C1.group_id
FROM chats C1
  INNER JOIN (SELECT
      C2.created_at,
      C2.chat_object,
      SUM(IF(C2.admin_read = 0, 1, 0)) AS unread
    FROM chats C2
    GROUP BY C2.chat_object) C2
    ON C1.chat_object = C2.chat_object
WHERE C1.group_id = 82
GROUP BY C1.chat_object,
         C1.group_id
ORDER BY C1.id DESC")->paginate(50);

But I am getting this: Call to a member function paginate() on array

LaraBABA's avatar

@sr57 Do you mean:

  C2.chat_object,
  SUM(IF(C2.admin_read = 0, 1, 0)) AS unread
FROM chats C2
GROUP BY C2.chat_object, C2.created_at

I am not sure I understood as I try to add MAX to C2.created_at but I am still have the same issue.

sr57's avatar

@user476820

correct syntax but not what you want from your original post

MAX(created_at) AS ... 
C2.chat_object,
SUM(IF(C2.admin_read = 0, 1, 0)) AS unread
FROM chats C2
GROUP BY C2.chat_object,

will give you the last date for each chat_object (if you want the last par mont see the link I posted before)

LaraBABA's avatar

@sr57 Been all day on it, I don't get it. Let's make it super short as I am not sure I explain the issue properly.

Let's say you have this setup:

TABLE 'CHATS'

ID | CHAT_ID | MESSAGES  |  UNREAD_MESSAGES | CREATED_AT
1   | POIPOIY-099-07 | hEY |  0  |  2022-06-02 13:16:42
2  | POIPOIY-099-07 | HI    |  0  |  2022-06-02 13:16:42
3  | POIPOIY-099-08 | HI    |  1   | 2022-06-02 13:16:42

And what I would like to achieve is:

-GROUP all the CHAT_UUID by their respective unique UUIDs but also get the latest messages "MESSAGES" per group. -SUM all the UNREAD_MESSAGES as 'IF 0 ? 1 : 0 (SUM( IF(C2.admin_read = 0, 1, 0))

I am not understanding if self referencing on the same table should be done via a join or sub query, I am honestly totally confused on this.

Took like 8 coffees, now starting wine as I am close to having a break-down ............:-( :-( :-( :-( :-(

sr57's avatar

@user476820

Let's make it super short

Good point

So

GROUP all the CHAT_UUID by their respective unique UUIDs

Assuming, UUID = chat_id :

GROUP BY chat_id;

Latest date :

SELECT MAX(created_at) AS last_date FROM chats  GROUP BY chat_id;

Latest message

= msg at the latest date :

SELECT r1.chat_id, r2.last_date, r1.messages FROM chats r1 
INNER JOIN (  SELECT chat_id,MAX(created_at) AS last_date FROM chats  GROUP BY chat_id ) r2 
ON r1.chat_id=r2.chat_id AND r1.created_at=r2.last_date;

SUM all the UNREAD_MESSAGES

Per group or not, I should assume it' s per group but sure you'll succeed to complete the query by yourself. Try chocolate, it works better (for me) than wine (in such situation)

LaraBABA's avatar

@sr57 Thank you so much(You are my master lol), let me try now :-)

LaraBABA's avatar

@sr57 LOL about chocolate :-)

I tried your query and I am having a different problem, the output is:

ID | CHAT_ID | MESSAGES  |  UNREAD_MESSAGES | CREATED_AT
1   | POIPOIY-099-07 | hEY |  0  |  2022-06-02 13:16:42
2  | POIPOIY-099-07 | HI    |  0  |  2022-06-02 13:16:42
3  | POIPOIY-099-08 | HI    |  1   | 2022-06-02 13:16:42
3  | POIPOIY-099-08 | HI    |  1   | 2022-06-02 13:16:42
3  | POIPOIY-099-09 | HI    |  1   | 2022-06-02 13:16:42

The "MESSAGES" ARE GOOD, all showing the latest one but the CHAT_ID is not grouped. I really was expecting it to be grouped with "FROM chats GROUP BY chat_id"

sr57's avatar

@user476820

Do you have some duplicate records (same chat_id, msg and date) ?

Probably yes, add DISTINCT at the beginning of your main query.

LaraBABA's avatar

@sr57 What a day! lol

Something is not right, And I think that's why I spent so much time on this, I have never struggled so much on a query before. Here is my new query based on yours:

SELECT DISTINCT
  C1.chat_id,
  C2.latest,
  C1.messages
FROM chats C1
  INNER JOIN (SELECT
      chats.chat_id,
      MAX(chats.created_at) AS latest
    FROM chats
    GROUP BY chats.chat_id) C2
    ON C1.chat_id = C2.chat_id
    AND C1.created_at = C2.latest

But the result is the same, the GROUP BY chats.chat_id of the INNER JOIN is not grouping them. I cannot work out why. I tried like 5 query builders today to get some help, they all give me the same result.

LaraBABA's avatar

This is my old query that works but when MYSQL strict mode is on, it fails:

                $sub = DB::table('chats as c')
                    ->select('messages')
                    ->whereColumn('c.chat_id', 'chats.chat_id')
                    ->limit(1)
                    ->latest();

                $history = DB::table('chats')->selectRaw(
                    'SUM(IF(admin_read = 0, 1, 0)) as unread, chat_id,  created_at, MAX(created_at) as latest'
                )
                    ->selectSub($sub, 'message')
                    ->where('group_id', '=', $request->userId)
                    ->orderBy('id', 'DESC')
                    ->groupBy('chat_id')
                    ->paginate(50);
sr57's avatar

@user476820

Should be a misunderstanding, please (re)share

  • a sample of input data

  • the result from the previous request

  • the expected result

PS : will be on line for only ~15mn

PS 2 : let strict mode on (we 'll be able to discuss of this point later)

LaraBABA's avatar

@sr57 Leave it for today ahahaha :-) I have already took so much energy out of you, it will take me some time to get a proper example running here: https://www.db-fiddle.com/

Thank you so much again for your time.

sr57's avatar

@user476820

Have a good night.

Will be less available next days but should be able to answer regarding the result you got and the expected result you expect.

sr57's avatar

@user476820

As you can see if you click on "run" it will show the Chat_id "0508133" as a duplicate rather than being grouped

It works well, you have simply 2 latest msg, same timestamp for the 2 msg for the same chat_id.

If you want only one line per chat_id for this case, you can use group_concat in mysql

https://www.mariadbtutorial.com/mariadb-aggregate-functions/mariadb-group_concat/

or you can choose to take only one (using max for instance), or you can avoid having this case in you app.

LaraBABA's avatar

@sr57 I will check this out tomorrow(it is 1 am), thank you so much! I will post back.

LaraBABA's avatar

@sr57 I was thinking, why the join is on the created_at and not on the id? As the Ids are incremental and can be reorganized by the latest it should be better no?

LaraBABA's avatar

Ok Got it I think

  C1.chat_id,
  C2.unread
FROM chats C1
  INNER JOIN (SELECT
      MAX(chats.id) AS latest_id,
      chats.chat_id,
      SUM(IF(chats.admin_read=0,1,0)) AS unread
    FROM chats
    GROUP BY chats.chat_id) C2
    ON C1.chat_id = C2.chat_id
    AND C1.id = C2.latest_id
GROUP BY C1.chat_id,
         C1.message,
         C1.updated_at,
         C2.unread
sr57's avatar

@user476820

I was thinking, why the join is on the created_at and not on the id?

Yes good point, Always better after a good night ... and chocolate ?

And, by the way, you found how 'non strict mode' runs under the hook (but let imagine you were using updated_at , ... with two values identical, no reason that id should be the last in this case).

Your sql sounds right, don't understand why you group by updated_at (and message) but it's your app.

LaraBABA's avatar

@sr57 Yesterday I felt like dead meat and when it is like that...my brain does not think anymore as I worked 7 days and 12 hours each day. Instead of Chocolate I take GABA + B6 together(when mega stressed with work), look for it online, it is magical(but I ran out this week). And when I want to push myself hard on low moral days I take the opposite which are "Mucuna pruriens".

By the way, about C1.message, C1.updated_at,

It is a mistake, my visual query builder added them as default. Each time I do a group, it adds all the selected fields in the group, no idea why this happens all the time. Good night and thanks again for all your help! Not sure what I should do about the best answer as I would like to give you credits too, please let me know what I should add as a best answer.

LaraBABA's avatar

I just changed the query as an eloquent query and as I said earlier, when strict mode is on I get this: Syntax error or access violation: 1055 'C1.updated_at' isn't in GROUP BY in C:\laragon\www\home\vendor\doctrine\dbal\lib\Doctrine\DBAL\Driver\PDOConnection.php:64 Stack trace:

When strict mode if off, it works.

sr57's avatar
sr57
Best Answer
Level 39

@user476820

my visual query builder added them as default. Each time I do a group, it adds all the selected fields in the group, no idea why this happens all the time.

Always same answer :-) all your select fields must be grouped or used in an aggregate function.

In fact, you did not share the beginning of your sql, I assume it was only "SELECT' ... yes? If not see above.

I just changed the query as an eloquent query

Not so obvious (sometimes) have a look to the sql generated. If you don't understand, like before it's another question, open a new thread.

LaraBABA's avatar

@sr57 Ok, for the grouping issues, I checked on stack overflow and people are saying that Laravel should be set this way:

        'mysql' => [
            'driver' => 'mysql',
            'url' => env('DATABASE_URL'),
            'host' => env('DB_HOST', '127.0.0.1'),
            'port' => env('DB_PORT', '3306'),
            'database' => env('DB_DATABASE', 'forge'),
            'username' => env('DB_USERNAME', 'forge'),
            'password' => env('DB_PASSWORD', ''),
            'unix_socket' => env('DB_SOCKET', ''),
            'charset' => 'utf8mb4',
            'collation' => 'utf8mb4_unicode_ci',
            'prefix' => '',
            'prefix_indexes' => true,
            'strict' => true,
            'modes' => [
                //'ONLY_FULL_GROUP_BY', // Disable this to allow grouping by one column
                'STRICT_TRANS_TABLES',
                'NO_ZERO_IN_DATE',
                'NO_ZERO_DATE',
                'ERROR_FOR_DIVISION_BY_ZERO',
                'NO_AUTO_CREATE_USER',
                'NO_ENGINE_SUBSTITUTION'
            ],
            'engine' => 'InnoDB',
            'options' => extension_loaded('pdo_mysql') ? array_filter([
                PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
            ]) : [],
            PDO::ATTR_PERSISTENT => true,
        ],

After that, no more errors on grouping with 1 column and my script started to work a lot better. Here more information about it: https://dev.mysql.com/doc/refman/5.7/en/group-by-handling.html#:~:text=If%20the%20ONLY_FULL_GROUP_BY%20SQL%20mode,are%20functionally%20dependent%20on%20them.

sr57's avatar

@user476820

Not clear.

What sql ?

  • raw or eloquent?
  • the one from your original question ?

What

  • was your previous mysql config ?
  • was changed?

Your link deals with the same point we are discussing from the very beginning (no "simple field" in a select with a group by)

To summarize ,

  1. original question : non strict mode + right (standard) sql should work

  2. if not, it's a second problem that could be the mysql config. If your config solved, it's good but if you tell me that this config solves your original question ...

That's said, if it's works, should be time to (re)close this thread?

1 like
LaraBABA's avatar

@sr57 Sorry I could not reply earlier....Covid got me and I ended up pretty bad....I wanted to say thank you for everything.

LaraBABA's avatar

@sr57 Thanks, I was not really sure what to add as the best answer. I chose the Mysql settings in the config as this is what seems to have stopped the groupBy Issues I was having.

sr57's avatar

@user476820

It's the only way to close a post and a good practice is not to choose one of your post :-)

https://laracasts.com/discuss/channels/general-discussion/guidelines-for-posting-on-laracastscom

Accept answers

If someone has taken the time to help you out, the least you can do is show your appreciation by marking it as solved. To do this just click the check/tick ✓ mark. Do not mark your own response as answered unless you've actually taken the time to format a correct answer, you won't gain experience points either.

LaraBABA's avatar

@sr57 Oh sorry I see, the best answer is based on appreciation and not on what solved the issue, sorry, this is opposite of Stack overflow. Ok, so in this case not sure, I will choose anything you answered because the actual original issue was the mysql settings and not the queries. When I ran my old queries, everything worked ok with these settings.

Tray2's avatar

@User476820 It is usually a good practice to give the reply that helped you arrive to the final solution the best reply, and it's also a good idea to show the finished solution to the problem as well.

LaraBABA's avatar

@Tray2 Thanks I posted the full solution above already which has:

            'modes' => [
                //'ONLY_FULL_GROUP_BY', // <----Disable this to allow grouping by one column
                'STRICT_TRANS_TABLES',
                'NO_ZERO_IN_DATE',
                'NO_ZERO_DATE',
                'ERROR_FOR_DIVISION_BY_ZERO',
                'NO_AUTO_CREATE_USER',
                'NO_ENGINE_SUBSTITUTION'
            ],

I was unable to have a single groupBy using a single column, no matter what I was trying from the above answers. What solved the issue is what they told me to do on Stackoverflow which is //'ONLY_FULL_GROUP_BY', // <----Disable this to allow grouping by one column . My entire app started to work again. Now what I am not sure about is....this solved my issue yes, but should users do this? From what people say on Stackoverflow, yes, it is fine. But is it really? This is why I am confused about what should be the correct answer.

Tray2's avatar

@User476820 I would not have disabled that, but rather rewritten the query. However that is your decision.

sr57's avatar

@Tray2

I agree with you, but it's not easy for everyone to understand the good use of "group by" when mysql* allows, with the non-strict mode, to create "simpler" queries that seem/can work. And when, on top of that, you want to create a "Laravel query" it can become a nightmare.

*better to use postgresql.

LaraBABA's avatar

@sr57 Any idea why in a database you should not use groupBy for a single column? What is the technical issue behind it? This is the part I am not understanding at all(if mysql technically allows you to do it, and it works).

Tray2's avatar

@User476820 If I'm not completely wrong, the SQL standard requires all columns in the group by, changing that is more of a lazy hack,

LaraBABA's avatar

@sr57 My current query completely changed from the original as It has been some time now.

But for example, let's say you have this:

select
id1,
id2,
id3
FROM chats
where id1 = ?
GROUP BY
id1,
id2,
id3

VS This:

select
id1,
id2,
id3
FROM chats
where id1 = ?
GROUP BY
id1

Why would the first groupBy gives no warnings and the second does(unless you uncomment this little settings).

The reason this is super annoying is....if you have select * and have a huge table, you have to add each all of the selected columns to the groupBy or get this warning. Making life so much more complicated as you have to write a lot more. I see this as strange and cannot understand the technical need behind this.

Let's say you have apples, oranges and bananas, why would you need to tell the script that you need to groupBy by "apples, oranges and bananas", you could just tell it to group all the columns by "apples" and not look at the other columns as you are only interested in grouping by Apples.

I hope I explained the question properly :-)

sr57's avatar

@user476820

We do not need bananas :-), let''s see with apples (id1) and oranges (id2)

You can group by apples only

select id1 FROM chats GROUP BY id1;

But id2 is "undefined", there is no sense to select it.

Let's take this data

id1   id2
11     21
11     22
12     21
select id1 FROM chats GROUP BY id1;
// 11
// 12

What should be id2 facing id1=11 : 21 or 22 ? "undefined"

What you can do is , use an aggregate function, your can calculate the min, max, count, sum ... of id2

select id1,SUM(id2) FROM chats GROUP BY id1;
// 11 , 43
// 12 , 21

Hope it's clear.

To resumerize this long thread

  • all your select fields must be grouped or used in an aggregate function

  • use "strict mode = true" with mysql (or use postgresql) to force the previous rule

Please or to participate in this conversation.