简体   繁体   中英

MySQL: select last set of records in each month

Several times per month, a new volley of records is added to the table certificant_region_stats with an identical timestamp, certificants_modified. I'm trying to select all records from the last volley per month.

My attempts crash my console:

SELECT * 
FROM certificant_region_stats 
WHERE certificants_modified
    IN (
        SELECT   MAX(certificants_modified)
        FROM     certificant_region_stats
        GROUP BY YEAR(certificants_modified), MONTH(certificants_modified)
    ) 
ORDER BY certificants_modified DESC;

The subquery works fine on its own, so I'm guessing I'm using IN wrong?

EDIT: sample data

| id | country | certificants_number | certificants_modified | 
==============================================================
| 1  | USA     | 2                   | 2015-06-22 10:24:55   |
| 2  | Canada  | 4                   | 2015-06-22 10:24:55   |
| 3  | USA     | 5                   | 2015-06-09 09:18:24   |
| 4  | Canada  | 6                   | 2015-06-09 09:18:24   |
| 5  | USA     | 8                   | 2015-05-15 11:30:00   |
| 6  | Canada  | 8                   | 2015-05-15 11:30:00   |
| 7  | USA     | 3                   | 2015-05-02 08:45:01   |
| 8  | Canada  | 7                   | 2015-05-02 08:45:01   |

I'd expect it to return * from rows 1, 2, 5 and 6.

One good strategy to SELECT the best row within a group is to LEFT JOIN a table to itself with all the group columns equal and then find a row that has no better row. It might look like this:

SELECT crs.*
FROM certificant_region_stats AS crs
LEFT JOIN certificant_region_stats AS better
ON YEAR(crs.certificants_modified) = YEAR(better.certificants_modified)
   AND MONTH(crs.certificants_modified) = MONTH(better.certificants_modified)
   AND crs.country = better.country
   AND crs.certificants_modified < better.certificants_modified
WHERE better.id IS NULL

Or you can use the LAST_DAY function to keep the year and month and discard the day and time, like so:

SELECT crs.*
FROM certificant_region_stats AS crs
LEFT JOIN certificant_region_stats AS better
ON LAST_DAY(crs.certificants_modified) = LAST_DAY(better.certificants_modified)
   AND crs.country = better.country
   AND crs.certificants_modified < better.certificants_modified
WHERE better.id IS NULL

Code in this answer is dual licensed: CC BY-SA 3.0 or the MIT License as published by OSI .

Here's a solution. It uses an expression to grab the first day of each month, then extracts your last batch of data for that month.

SELECT DATE(DATE_FORMAT(crs.certificants_modified, '%Y-%m-01')) month_begin,
       crs.id, crs.country, crs.certificants_modified
  FROM certificant_region_stats crs
  JOIN (
       SELECT MAX(certificants_modified) lastmod,
              DATE(DATE_FORMAT(certificants_modified, '%Y-%m-01')) month_begin
         FROM certificant_region_stats
        GROUP BY DATE(DATE_FORMAT(certificants_modified, '%Y-%m-01'))
        ) last ON crs.certificants_modified = last.lastmod
 ORDER BY month_begin, crs.id

See this. http://sqlfiddle.com/#!9/3b386/3/0

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