1

I'm trying to figure out the appropriate way of returning the posts & the total number of posts of a user in one query.

  1. So the simplest way of counting the total number of posts of a user would be:

    SELECT COUNT(id) as total FROM posts WHERE uID = 37;
    
    +-------+
    | total |
    +-------+
    | 10    |
    +-------+
    
  2. Next, I've changed the query to return the ID of the posts and limit the total results to the first 5 posts. But MySQL threw Error 1140...

    SELECT id, COUNT(id) as total FROM posts WHERE uID = 37 LIMIT 0,5;
    
    Error Code: 1140. In aggregated query without GROUP BY, expression #1 of SELECT list contains nonaggregated column 'db.posts.id'; this is incompatible with sql_mode=only_full_group_by
    
  3. Okay, No hard feelings... I'm not planning to change the default 'sql_mode' so I've fixed the above error by adding the GROUP BY clause to the query as suggested, but now the COUNT() function doesn't make much sense, because it returns 1 for each result... Hmmm

    SELECT id, COUNT(id) as total FROM posts WHERE uID = 37 GROUP BY id LIMIT 0,5;
    
    +----+-------+
    | id | total |
    +------------+
    |  1 | 1     |
    +----+-------+
    |  2 | 1     |
    +----+-------+
    |  3 | 1     |
    +----+-------+
    |  4 | 1     |
    +----+-------+
    |  5 | 1     |
    +----+-------+
    
  4. Well, then I've added a JOIN clause for the posts so I can count the posts of the user, however I'm not very satisfied with the query, because of the additional JOIN clause and because the results has the 'total' field repeating in each row.

    SELECT id, userPosts.total as total FROM posts
        JOIN (SELECT COUNT(*) AS total FROM posts WHERE uID = 37) AS userPosts
    WHERE uID = 37 
    GROUP BY id, userPosts.total
    LIMIT 0,5;
    
    +----+-------+
    | id | total |
    +------------+
    |  1 | 10    |
    +----+-------+
    |  2 | 10    |
    +----+-------+
    |  3 | 10    |
    +----+-------+
    |  4 | 10    |
    +----+-------+
    |  5 | 10    |
    +----+-------+
    

So I'm thinking to myself.. there must be a better way of returning the posts & the total number of posts of a user in one query, so the MySQL result object would look something like this: (without the total in each post object) but I cannot find any ways.

{
  results: [
    {
      id: 1
    }, {
      id: 2
    }, {
      id: 3
    }, {
      id: 4
    }, {
      id: 5
    }
  ],
  total: 10
}

Perhaps I should create two separate MySQL queries, one for the posts of a user and another for the totals, and concatenate the two responses using a helper function on the server-side?

What do u guys suggest? Should I create two separate calls to get the results as specified above or is there any better way to improve my query?

Csaba
  • 1,945
  • 3
  • 28
  • 46
  • Why not just return all the posts then count the number of posts in the application? (e.g. in `fetch` something like `$count++` or take count of an array if you storing to an array) The issue with your group by is you're grouping by the record id rather than userid. If you group by userid you'll get the count back. You could do the `count` query first the `union` a retrieval for the post data but that doesn't seem to useful. – user3783243 Oct 22 '20 at 08:43
  • 1
    `JSON_ARRAYAGG(JSON_OBJECT('id', id))` – Akina Oct 22 '20 at 08:48
  • @user3783243 I think returning all the posts just for counting reasons is unneeded, and it would probably overload the server if the user has way too many posts. – Csaba Oct 22 '20 at 09:17
  • Well you said you need the posts, no? `I'm trying to figure out the appropriate way of returning the posts ` if you don't need the posts but rather a count than yea that can be simplified. If you need each posts data though there's no way to get around that. – user3783243 Oct 22 '20 at 10:05

1 Answers1

2

You are describing a window count. In MySQL 8.0, you would do:

select id, count(*) over() as total 
from posts
where uid = 37 

If you are running an earlier version, then the [cross] join is fine. You could also express this with a correlated subquery - but the logic is the same:

select id, (select count(*) from posts p1 where p1.uid = p.uid) as total 
from posts p
where uid = 37 
GMB
  • 216,147
  • 25
  • 84
  • 135
  • Yes, I was thinking about upgrading to version 8, but v8 uses a different authentication method and it is not yet supported within the mysql library that I'm using in Node.js. – Csaba Oct 22 '20 at 08:53
  • I've tried the your 'correlated subquery' and it works fine without having to use the GROUP BY clause on the query. – Csaba Oct 22 '20 at 08:58