简体   繁体   中英

MySQL - Group by non aggregate column

If I'm storing my user's scores like so:

+---------+-------+------------+
| user_id | score | created_at |
+---------+-------+------------+
|       1 |   100 | 2017-12-20 |
|       1 |   200 | 2017-12-21 |
|       2 |   110 | 2017-12-20 |
|       2 |   210 | 2017-12-21 |
|       3 |   120 | 2017-12-20 |
|       3 |   220 | 2017-12-21 |
+---------+-------+------------+

How would I be able to get the closest record for each user given an input date?

I got as far as doing

SELECT *, (abs(datediff("$some-input-date", created_at))) as diff FROM table order by diff

Which would give me for an input date 2017-12-19 :

+---------+-------+------------+------+
| user_id | score | created_at | diff |
+---------+-------+------------+------+
|       1 |   100 | 2017-12-20 |    1 |
|       2 |   110 | 2017-12-20 |    1 |
|       3 |   120 | 2017-12-20 |    1 |
|       1 |   200 | 2017-12-21 |    2 |
|       2 |   210 | 2017-12-21 |    2 |
|       3 |   220 | 2017-12-21 |    2 |
+---------+-------+------------+------+

Now I'd want the unique rows by user_id , so I assumed something like GROUP BY user_id would work but I get a "Expression of SELECT list is not in GROUP BY clause" error in MySQL 5.7. How do I group by user_id in this scenario?

(I'm also using doctrine, so if there is some way of achieving this using dql or doctrine functions that would also be useful)

OK, so you know how to get the date difference, and you want only the top result for a particular user, ordered by the date difference ascending:

SELECT * FROM
--your current query
(SELECT *, (abs(datediff("$some-input-date", created_at))) as diff FROM table) as data_with_diffs
INNER JOIN
( --a query to find only the minimum diffs per user id
 SELECT userID, MIN(abs(datediff("$some-input-date", created_at))) as min_diff 
 FROM table 
 GROUP BY userid
) as find_min_diffs

ON 
  data_with_diffs.userid = find_min_diffs.userid AND
  data_with_diffs.diff = find_min_diffs.min_diff

If you run the two inner queries in isolation you'll see how it works. There are other ways of structuring this but I figured this would be best for you to see how the whole thing hangs together based on what you unsderstand/already developed

The grouping query selects only the minimum diff for a particular user id. By running this as a subquery, and joining it back to the data you already generated, the INNER JOIN will filter out all the rows where the diff is not equal to the minimum diff

You might still get repeated rows for a user, if they have a date before and a date after with the same diff (ie -1 and +1 - these are both nearest) so you might have to implement a strategy to deal with that, like picking their MAX score

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM