20

I have a table that contains multiple records for each day of the month, over a number of years. Can someone help me out in writing a query that will only return the last day of each month.

Matt
  • 4,140
  • 9
  • 40
  • 64

9 Answers9

35

SQL Server (other DBMS will work the same or very similarly):

SELECT
  *
FROM
  YourTable
WHERE
  DateField IN (
    SELECT   MAX(DateField)
    FROM     YourTable
    GROUP BY MONTH(DateField), YEAR(DateField)
  )

An index on DateField is helpful here.

PS: If your DateField contains time values, the above will give you the very last record of every month, not the last day's worth of records. In this case use a method to reduce a datetime to its date value before doing the comparison, for example this one.

Community
  • 1
  • 1
Tomalak
  • 332,285
  • 67
  • 532
  • 628
  • 1
    For **PostgreSQL** you need to use: `GROUP BY EXTRACT(MONTH FROM DateField), EXTRACT(YEAR FROM DateField)`. – Tadeu Sampaio Jun 03 '21 at 11:38
  • if it's can help someone i just convert mydatetime to date then i did a compariaosn `SELECT * FROM Table_ WHERE CONVERT(VARCHAR(10), yourDateTime, 111) IN ( SELECT MAX(CONVERT(VARCHAR(10), yourDateTime, 111)) AS LastDay FROM Table_ GROUP BY YEAR(yourDateTime), MONTH(yourDateTime))` – satcha Sep 17 '21 at 17:24
11

The easiest way I could find to identify if a date field in the table is the end of the month, is simply adding one day and checking if that day is 1.

where DAY(DATEADD(day, 1, AsOfDate)) = 1

If you use that as your condition (assuming AsOfDate is the date field you are looking for), then it will only returns records where AsOfDate is the last day of the month.

Javier Rapoport
  • 123
  • 1
  • 7
  • I found this to be the cleanest solution for me; what's the best way to include the row for the last date in the table? where (DAY(DATEADD(day, 1, AsOfDate)) = 1 or AsOfDate = (select max(AsOfDate) from [Table]))? – ScottFoster1000 Jan 10 '18 at 22:49
  • That should do it, yeah. That way you would return either the last day of the month or the last day you have available. Only problem is that you might end up with multiple results, not sure if that's a concern or not – Javier Rapoport Jan 12 '18 at 00:23
  • Thanks, good point re multiple results; just to note, in my final solution, I set the max datetime to a variable at the top of the script and use that in the where test for performance. – ScottFoster1000 Jan 12 '18 at 21:00
  • Cleanest solution to me. – cimentadaj Apr 21 '18 at 12:48
  • This @JavierRapoport answer will give **wrong results if the database contains only weekdays**. The @Tomalak selected answer above with `GROUP BY` is better because it will always give the correct results, regardless of the data on the database. – Tadeu Sampaio Jun 03 '21 at 11:42
  • @TadeuSampaio - agreed that it's not a generic answer, but based on the question as it was presented ("multiple records for each day of the month"), then assuming that there's a record for each day is not a bad assumption, and this logic is the simplest without having to rely in grouping/subquerying – Javier Rapoport Jun 05 '21 at 01:07
5

Use the EOMONTH() function if it's available to you (E.g. SQL Server). It returns the last date in a month given a date.

select distinct
Date 
from DateTable
Where Date = EOMONTH(Date)

Or, you can use some date math.

select distinct
Date
from DateTable
where Date = DATEADD(MONTH, DATEDIFF(MONTH, -1, Date)-1, -1)
FistOfFury
  • 6,735
  • 7
  • 49
  • 57
2

In SQL Server, this is how I usually get to the last day of the month relative to an arbitrary point in time:

select dateadd(day,-day(dateadd(month,1,current_timestamp)) , dateadd(month,1,current_timestamp) )

In a nutshell:

  1. From your reference point-in-time,
  2. Add 1 month,
  3. Then, from the resulting value, subtract its day-of-the-month in days.

Voila! You've the the last day of the month containing your reference point in time.

Getting the 1st day of the month is simpler:

select dateadd(day,-(day(current_timestamp)-1),current_timestamp)
  1. From your reference point-in-time,
  2. subtract (in days), 1 less than the current day-of-the-month component.

Stripping off/normalizing the extraneous time component is left as an exercise for the reader.

Nicholas Carey
  • 71,308
  • 16
  • 93
  • 135
0

This should work on Oracle DB

select  distinct last_day(trunc(sysdate - rownum)) dt
    from dual
  connect by rownum < 430
   order by 1
0

I did the following and it worked out great. I also wanted the Maximum Date for the Current Month. Here is what I my output is. Notice the last date for July which is 24th. I pulled it on 7/24/2017, hence the result

Year    Month   KPI_Date
2017    4       2017-04-28
2017    5       2017-05-31
2017    6       2017-06-30
2017    7       2017-07-24


    SELECT  B.Year ,
    B.Month ,
    MAX(DateField) KPI_Date
FROM    Table A
    INNER JOIN ( SELECT  DISTINCT
                        YEAR(EOMONTH(DateField)) year ,
                        MONTH(EOMONTH(DateField)) month
                 FROM   Table
               ) B ON YEAR(A.DateField) = B.year
                      AND MONTH(A.DateField) = B.Month
GROUP BY B.Year ,
    B.Month 
Amit
  • 559
  • 5
  • 8
0
SELECT * FROM YourTableName WHERE anyfilter 
AND "DATE" IN (SELECT MAX(NameofDATE_Column) FROM YourTableName WHERE
anyfilter GROUP BY
TO_CHAR(NameofDATE_Column,'MONTH'),TO_CHAR(NameofDATE_Column,'YYYY'));

Note: this answer does apply for Oracle DB

Shekhar Nalawade
  • 127
  • 1
  • 1
  • 15
0

Here's how I just solved this. day_date is the date field, calendar is the table that holds the dates.

SELECT cast(datepart(year, day_date) AS VARCHAR) 
+ '-' 
+ cast(datepart(month, day_date) AS VARCHAR) 
+ '-' 
+ cast(max(DATEPART(day, day_date)) AS VARCHAR) 'DATE'
FROM calendar
GROUP BY datepart(year, day_date)
    ,datepart(month, day_date)
ORDER BY 1
jw11432
  • 545
  • 2
  • 20
0

A simple way to get the last day of month is to get the first day of the next month and subtract 1.

Aaron Digulla
  • 321,842
  • 108
  • 597
  • 820