96

I want to get the records of last month based on my database table [member] field date_created.

How can I use SQL to do this?

For clarification, last month - 1/8/2009 to 31/8/2009

If today is 3/1/2010, I'll need to get the records of 1/12/2009 to 31/12/2009.

3
  • 2
    Does "last month" mean the "last 30 days", "all days in previous month" or "all days in current month"?
    – Cellfish
    Commented Sep 15, 2009 at 3:43
  • The best syntax also depends on if you're using SQL 2005 (or earlier) with a DATETIME field, or SQL 2008 with a DATE field.
    – eidylon
    Commented Sep 15, 2009 at 4:15
  • 5
    What about DATEDIFF(month, date_created, GETDATE()) = 1? Wouldn't that work as well?
    – Philipp M
    Commented Nov 6, 2014 at 10:13

23 Answers 23

148

All the existing (working) answers have one of two problems:

  1. They will ignore indices on the column being searched
  2. The will (potentially) select data that is not intended, silently corrupting your results.

1. Ignored Indices:

For the most part, when a column being searched has a function called on it (including implicitly, like for CAST), the optimizer must ignore indices on the column and search through every record. Here's a quick example:

We're dealing with timestamps, and most RDBMSs tend to store this information as an increasing value of some sort, usually a long or BIGINTEGER count of milli-/nanoseconds. The current time thus looks/is stored like this:

1402401635000000  -- 2014-06-10 12:00:35.000000 GMT

You don't see the 'Year' value ('2014') in there, do you? In fact, there's a fair bit of complicated math to translate back and forth. So if you call any of the extraction/date part functions on the searched column, the server has to perform all that math just to figure out if you can include it in the results. On small tables this isn't an issue, but as the percentage of rows selected decreases this becomes a larger and larger drain. Then in this case, you're doing it a second time for asking about MONTH... well, you get the picture.

2. Unintended data:

Depending on the particular version of SQL Server, and column datatypes, using BETWEEN (or similar inclusive upper-bound ranges: <=) can result in the wrong data being selected. Essentially, you potentially end up including data from midnight of the "next" day, or excluding some portion of the "current" day's records.

What you should be doing:

So we need a way that's safe for our data, and will use indices (if viable). The correct way is then of the form:

WHERE date_created >= @startOfPreviousMonth AND date_created < @startOfCurrentMonth

Given that there's only one month, @startOfPreviousMonth can be easily substituted for/derived by:

DATEADD(month, -1, @startOfCurrentMonth)

If you need to derive the start-of-current-month in the server, you can do it via the following:

DATEADD(month, DATEDIFF(month, 0, CURRENT_TIMESTAMP), 0)

A quick word of explanation here. The initial DATEDIFF(...) will get the difference between the start of the current era (0001-01-01 - AD, CE, whatever), essentially returning a large integer. This is the count of months to the start of the current month. We then add this number to the start of the era, which is at the start of the given month.

So your full script could/should look similar to the following:

DECLARE @startOfCurrentMonth DATETIME
SET @startOfCurrentMonth = DATEADD(month, DATEDIFF(month, 0, CURRENT_TIMESTAMP), 0)

SELECT *
FROM Member
WHERE date_created >= DATEADD(month, -1, @startOfCurrentMonth)
      AND date_created < @startOfCurrentMonth

All date operations are thus only performed once, on one value; the optimizer is free to use indices, and no incorrect data will be included.

3
  • 1
    Very good answer, thank you! I'd like to suggest you to extract "DATEADD(month, -1, @startOfCurrentMonth)" to another variable, perhaps "@startOfLastMonth", in order to avoid the optimizer doing this calculation for every iteration. I tryed to collaborate editing this answer by myself, but its edit queue if full. Commented Nov 22, 2021 at 21:44
  • @YorgaBabuscan - If by "iteration" you mean "comparison for row", the calculation is recognized by the optimizer to be operating on a constant value, and is only calculated once. If it was calculated for each row, it wouldn't be able to use an index for the lower bound comparison at all, which would be very detrimental. Commented Nov 23, 2021 at 1:11
  • With SQL Server 2022 and later, the DATETRUNC() function can be used to simplify the calculation. The resulting code would be DECLARE @startOfCurrentMonth DATETIME = DATETRUNC(month, CURRENT_TIMESTAMP), DECLARE @startOfPreviousMonth DATETIME = DATEADD(month, -1, @startOfCurrentMonth), and WHERE date_created >= @startOfPreviousMonth AND date_created < @startOfCurrentMonth. (And just as a reminder to the reader, < must be used instead of <= because the end-date is exclusive. This is also why BETWEEN should not be used.)
    – T N
    Commented Nov 17, 2023 at 15:52
130
SELECT * 
FROM Member
WHERE DATEPART(m, date_created) = DATEPART(m, DATEADD(m, -1, getdate()))
AND DATEPART(yyyy, date_created) = DATEPART(yyyy, DATEADD(m, -1, getdate()))

You need to check the month and year.

4
  • 2
    in my case SELECT * FROM Member WHERE DATEPART(m, date_created) = DATEPART(m, DATEADD(m, -1, getdate())) AND DATEPART(yy, date_created) = DATEPART(yy, DATEADD(m, -1, getdate())) works perfect
    – gofor.net
    Commented May 22, 2012 at 10:20
  • 15
    This approach will ignore any indexes on the table and do a table (or clustered index) scan every time it is run. The larger the table gets the longer the query will take.
    – mrdenny
    Commented Aug 5, 2012 at 19:19
  • 5
    You must use yyyy instead of y
    – inser
    Commented Feb 19, 2013 at 9:05
  • This should not be the accepted answer. Using YEAR(), MONTH(), DATEPART() or similar functions on the date being filtered eliminates any possibility that SQL server can optimize the query to use an index on that date. This bad practice should be strongly discouraged.
    – T N
    Commented Nov 17, 2023 at 15:58
13

Add the options which have been provided so far won't use your indexes at all.

Something like this will do the trick, and make use of an index on the table (if one exists).

DECLARE @StartDate DATETIME, @EndDate DATETIME
SET @StartDate = dateadd(mm, -1, getdate())
SET @StartDate = dateadd(dd, datepart(dd, getdate())*-1, @StartDate)
SET @EndDate = dateadd(mm, 1, @StartDate)

SELECT *
FROM Member
WHERE date_created BETWEEN @StartDate AND @EndDate
3
  • Sorry to downvote, but it doesn't work: Try '12/31/2013 11:59' for a counterexample (returns rows between '2013-10-30 11:59:00.000' and '2013-11-30 11:59:00.000' on SQL Server). Commented Jul 15, 2013 at 19:15
  • 1
    getdate() give you current time. I think you should take time from 12:00 AM (starting of day). @Rokas answer is correct. Print start time and end time if you want to see. Commented Feb 23, 2016 at 19:28
  • This solution should not be used for two reasons. The @StartDate and @EndDate calculations do not eliminate the time component. Even if they did, the use of BETWEEN would incorrectly include midnight (00:00:00) of the date following the intended date range.
    – T N
    Commented Nov 17, 2023 at 16:02
12
DECLARE @StartDate DATETIME, @EndDate DATETIME
SET @StartDate = DATEADD(mm, DATEDIFF(mm,0,getdate())-1, 0)
SET @EndDate = DATEADD(mm, 1, @StartDate)

SELECT *
FROM Member
WHERE date_created BETWEEN @StartDate AND @EndDate

An upgrade to mrdenny's solution, this way you get exactly last month from YYYY-MM-01

1
  • BETWEEN should not be used because it would incorrectly include midnight (00:00:00) of the date following the intended date range. Instead, the test should be written as WHERE date_created >= @StartDate AND date_created < @EndDate. Note the use of < instead of <=.
    – T N
    Commented Nov 17, 2023 at 16:06
5

Last month consider as till last day of the month. 31/01/2016 here last day of the month would be 31 Jan. which is not similar to last 30 days.

SELECT CONVERT(DATE, DATEADD(DAY,-DAY(GETDATE()),GETDATE()))
2

SQL query to get record of the present month only

SELECT * FROM CUSTOMER
WHERE MONTH(DATE) = MONTH(CURRENT_TIMESTAMP) AND YEAR(DATE) = YEAR(CURRENT_TIMESTAMP);
1
  • Using YEAR(), MONTH(), DATEPART() or similar functions on the date being filtered eliminates any possibility that SQL server can optimize the query to use an index on that date. This bad practice should be strongly discouraged.
    – T N
    Commented Nov 17, 2023 at 16:15
2
SELECT * FROM Member WHERE month(date_created) = month(NOW() - INTERVAL 1 MONTH);
3
  • Code dumps do not make for good answers. You should explain how and why this solves their problem. I recommend reading, How do I write a good answer?
    – Amir Dora.
    Commented Feb 17, 2021 at 13:52
  • "WHERE myDate > (NOW() - INTERVAL 1 MONTH)" allowed me to get all of the records for myDate within the past month, thanks for the tip
    – Josh McGee
    Commented Nov 18, 2021 at 16:29
  • Using YEAR(), MONTH(), DATEPART() or similar functions on the date being filtered eliminates any possibility that SQL server can optimize the query to use an index on that date. This bad practice should be strongly discouraged.
    – T N
    Commented Nov 17, 2023 at 16:15
1

One way to do it is using the DATEPART function:

select field1, field2, fieldN from TABLE where DATEPART(month, date_created) = 4 
and DATEPART(year, date_created) = 2009

will return all dates in april. For last month (ie, previous to current month) you can use GETDATE and DATEADD as well:

select field1, field2, fieldN from TABLE where DATEPART(month, date_created) 
= (DATEPART(month, GETDATE()) - 1) and 
DATEPART(year, date_created) = DATEPART(year, DATEADD(m, -1, GETDATE()))
2
  • 3
    This approach will ignore any indexes on the table and do a table (or clustered index) scan every time it is run. The larger the table gets the longer the query will take.
    – mrdenny
    Commented Aug 5, 2012 at 19:20
  • Using YEAR(), MONTH(), DATEPART() or similar functions on the date being filtered eliminates any possibility that SQL server can optimize the query to use an index on that date. This bad practice should be strongly discouraged.
    – T N
    Commented Nov 17, 2023 at 16:15
1
declare @PrevMonth as nvarchar(256)

SELECT @PrevMonth = DateName( month,DATEADD(mm, DATEDIFF(mm, 0, getdate()) - 1, 0)) + 
   '-' + substring(DateName( Year, getDate() ) ,3,4)
0
select * from [member] where DatePart("m", date_created) = DatePart("m", DateAdd("m", -1, getdate())) AND DatePart("yyyy", date_created) = DatePart("yyyy", DateAdd("m", -1, getdate()))
1
  • 2
    While this code may answer the question, providing additional context regarding why and/or how this code answers the question improves its long-term value. Commented May 17, 2020 at 22:34
0
WHERE 
    date_created >= DATEADD(MONTH, DATEDIFF(MONTH, 31, CURRENT_TIMESTAMP), 0)
    AND date_created < DATEADD(MONTH, DATEDIFF(MONTH, 0, CURRENT_TIMESTAMP), 0)
1
  • A little bit of explanation? Commented Mar 11, 2013 at 21:07
0

I'm from Oracle env and I would do it like this in Oracle:

select * from table
where trunc(somedatefield, 'MONTH') =
trunc(sysdate -INTERVAL '0-1' YEAR TO MONTH, 'MONTH')

Idea: I'm running a scheduled report of previous month (from day 1 to the last day of the month, not windowed). This could be index unfriendly, but Oracle has fast date handling anyways. Is there a similar simple and short way in MS SQL? The answer comparing year and month separately seems silly to Oracle folks.

0

You can get the last month records with this query

SELECT * FROM dbo.member d 
WHERE  CONVERT(DATE, date_created,101)>=CONVERT(DATE,DATEADD(m, datediff(m, 0, current_timestamp)-1, 0)) 
and CONVERT(DATE, date_created,101) < CONVERT(DATE, DATEADD(m, datediff(m, 0, current_timestamp)-1, 0),101) 
0

I don't think the accepted solution is very index friendly I use the following lines instead

select * from dbtable where the_date >= convert(varchar(10),DATEADD(m, -1, dateadd(d, - datepart(dd, GETDATE())+1, GETDATE())),120) and the_date <= dateadd(ms, -3, convert(varchar(10),DATEADD(m, 0, dateadd(d, - datepart(dd, GETDATE())+1, GETDATE())),120));

Or simply (this is the best).

select * from dbtable where the_date >= convert(varchar(10),DATEADD(m, -1, dateadd(d, - datepart(dd, GETDATE())+1, GETDATE())),120) and the_date < SELECT convert(varchar(10),DATEADD(m, -1, dateadd(d, - datepart(dd, GETDATE())+1, GETDATE())),120);

Some help

-- Get the first of last month
SELECT convert(varchar(10),DATEADD(m, -1, dateadd(d, - datepart(dd, GETDATE())+1, GETDATE())),120);
-- Get the first of current month
SELECT convert(varchar(10),DATEADD(m, -1, dateadd(d, - datepart(dd, GETDATE())+1, GETDATE())),120);
--Get the last of last month except the last 3milli seconds. (3miliseconds being used as SQL express otherwise round it up to the full second (SERIUSLY MS)
SELECT dateadd(ms, -3, convert(varchar(10),DATEADD(m, 0, dateadd(d, - datepart(dd, GETDATE())+1, GETDATE())),120));
0
0

Here is what I did so I could put it in a view:

ALTER view [dbo].[MyView] as

with justdate as (
select getdate() as rightnow
)
, inputs as (
select dateadd(day, 1, EOMONTH(jd.rightnow, -2)) as FirstOfLastMonth
      ,dateadd(day, 1, EOMONTH(jd.rightnow, -1)) as FirstOfThisMonth
  from justdate jd
)
SELECT TOP 10000
       [SomeColumn]
      ,[CreatedTime]
  from inputs i
  join [dbo].[SomeTable]
    on createdtime >= i.FirstOfLastMonth
   and createdtime < i.FirstOfThisMonth
 order by createdtime
;

Note that I intentionally ran getdate() once.

-1
DECLARE @StartDate DATETIME, @EndDate DATETIME    
SET @StartDate = DATEADD(mm, DATEDIFF(mm, 0, getdate()) - 1, 0)    
SET @EndDate = dateadd(dd, -1, DATEADD(mm, 1, @StartDate))

SELECT * FROM Member WHERE date_created BETWEEN @StartDate AND @EndDate 

and another upgrade to mrdenny's solution.
It gives the exact last day of the previous month as well.

-2

In Sql server for last one month:

select * from tablename 
where order_date > DateAdd(WEEK, -1, GETDATE()+1) and order_date<=GETDATE()
1
  • Should perhaps WEEK be MONTH ?
    – mortb
    Commented Mar 24, 2022 at 12:43
-2
DECLARE @curDate INT = datepart( Month,GETDATE())
IF (@curDate = 1)
    BEGIN
        select * from Featured_Deal
        where datepart( Month,Created_Date)=12 AND datepart(Year,Created_Date) = (datepart(Year,GETDATE())-1)

    END
ELSE
    BEGIN
        select * from Featured_Deal
        where datepart( Month,Created_Date)=(datepart( Month,GETDATE())-1) AND datepart(Year,Created_Date) = datepart(Year,GETDATE())

    END 
-2
DECLARE @StartDate DATETIME, @EndDate DATETIME
SET @StartDate = dateadd(mm, -1, getdate())
SET @StartDate = dateadd(dd, datepart(dd, getdate())*-1, @StartDate)
SET @EndDate = dateadd(mm, 1, @StartDate)
set @StartDate = DATEADD(dd, 1 , @StartDate)
-2

The way I fixed similar issue was by adding Month to my SELECT portion

Month DATEADD(day,Created_Date,'1971/12/31') As Month

and than I added WHERE statement

Month DATEADD(day,Created_Date,'1971/12/31') = month(getdate())-1
-2

If you are looking for last month so try this,

SELECT
FROM  #emp 
WHERE DATEDIFF(MONTH,CREATEDDATE,GETDATE()) = 1

If you are looking for last month so try this,

SELECT
FROM #emp
WHERE DATEDIFF(day,CREATEDDATE,GETDATE()) between 1 and 30
2
  • 1
    Please read the previous answers to see why this wouldn't work and\or not the correct way to do it before people downvoting it. And maybe you would like to remove your reply. Commented Mar 23, 2019 at 7:34
  • Hi, welcome to Stack Overflow. When answering a question that already has many answers (17!), please be sure to add some additional insight into why the hastily composed response you're providing is substantive and not simply echoing what's already been vetted by the original poster. This is especially important in "code-only" answers like yours.
    – chb
    Commented Mar 23, 2019 at 8:05
-2

A simple query which works for me is:

select * from table where DATEADD(month, 1,DATEFIELD) >= getdate()

-2

If you are looking for previous month data:

date(date_created)>=date_sub(date_format(curdate(),"%Y-%m-01"),interval 1 month) and 
date(date_created)<=date_sub(date_format(curdate(),'%Y-%m-01'),interval 1 day)

This will also work when the year changes. It will also work on MySQL.

Not the answer you're looking for? Browse other questions tagged or ask your own question.