SQL Query for retreiving records that fall on the last day of the month - sql

I have a table with records from many dates.
I would like to perform a query that only returns records that fall on the last day of the date's month. Something like:
SELECT * FROM mytable
WHERE DATEPART(d, mytable.rdate) = DATEPART(d,DATEADD(m,1,DATEADD(d, -1, mytable.rdate)))
Except this doesn't work as the rdate in the right-hand side of the argument should be the last day of it's month for the dateadd to return the correct comparison.
Basically is there an concise way to do this comparison?
An equivalent for quarter-ends would also be very helpful, but no doubt more complex.
EDIT:
Essentially I want to check if a given date is the last in whatever month (or quarter) it falls into. If it is, return that record. This would involve a some function to return the last day of the month of any date. e.g. lastdayofmonth('2013-06-10') = 30 (so this record would not be returned.
EDIT2:
For the case of returning the records that fall on the last day of the quarter they are in it would need to be something like:
SELECT * FROM mytable
WHERE DATEPART('d', mytable.rdate) = lastdayofmonth(mytable.rdate)
AND DATEPART('m', mytable.rdate) = lastmonthofquarter(mytable.rdate);
The tricky bit is the lastdayofmonth() and lastmonthofquarter() functions

Use the DateSerial Function to compute the last day of the month for a given date.
Passing zero as the third argument, day, actually returns the last date of the previous month.
rdate = #2013-7-24#
? DateSerial(Year(rdate), Month(rdate), 0)
6/30/2013
So to get the last date from the rdate month, add 1 to the month argument.
? DateSerial(Year(rdate), Month(rdate) + 1, 0)
7/31/2013
You might suspect that approach would break for a December rdate, since Month() + 1 would return 13. However, DateSerial still copes with it.
rdate = #2013-12-1#
? DateSerial(Year(rdate), Month(rdate) + 1, 0)
12/31/2013
If you will be running your query from within an Access application session, you can build a VBA function based on that approach, and use the custom function in the query.
However, if the query will be run from an ODBC or OleDb connection to the Access db, the query can not use a VBA user-defined function. In that situation, you can use DateSerial directly in your query.
SELECT m.*
FROM mytable AS m
WHERE m.rdate = DateSerial(Year(m.rdate), Month(m.rdate) + 1, 0)
That should work if your rdate values all include midnight as the time component. If those values include other times, use DateValue.
WHERE DateValue(m.rdate) = DateSerial(Year(m.rdate), Month(m.rdate) + 1, 0)

Try This.
SELECT * FROM mytable
WHERE DATEPART(d, mytable.rdate) =
DATEADD(d, -1, DATEADD(m, DATEDIFF(m, 0, GETDATE()) + 1, 0))

Try this one, this is tested on MS Access:
Using String Concatenation:
SELECT * FROM mytable
WHERE
DatePart('d',mytable.rdate) =
DatePart('d',dateadd('m',1, "1/" & DatePart('m',mytable.rdate) & "/" & DatePart('yyyy',mytable.rdate))-1);
Update without using string concatenation:
SELECT * FROM
(SELECT *,
DATEPART('d',DATEDIFF('m',0,mytable.rdate)+1,
DATEADD('m',1,
DATEADD('d',DATEDIFF('d',0,mytable.rdate)- DATEPART('d',mytable.rdate)+1,0))-1) as EOMonth
FROM mytable ) A
WHERE DATEPART('d',mytable.rdate) = DATEPART('d',EOMonth)

Related

MS SQL How to get the next month if its december

Hi I wanna get the next month in SQL server but what if the month is 12.
when i have date = '2016-10-04' then the next month will be date = '2016-11-04'.
I want to put this into this query :
if EXISTS(
select * from month
where id_Prod = #id_Prod
and datepart(month,DATEADD(month,1,_date)) = datepart(month,DATEADD(month,1,_date))
and datepart(YEAR,_date) = datepart(YEAR,#date)
);
you can try dateadd
declare #dt date = getdate()
select datepart(MM,dateadd(mm,1, #dt))
If the spec I've given in the comments is correct, you want something along the lines of:
if EXISTS(
select * from month
where id_Prod = #id_Prod
and _date >= DATEADD(month,DATEDIFF(month,'20010101',#date),'20010201')
and _date < DATEADD(month,DATEDIFF(month,'20010101',#date),'20010301')
);
The DATEADD,DATEDIFF pairs are just being used to generate "the 1st of next month" and "the 1st of the month after that", using arbitrary (fixed) dates to compute those. E.g. the first line computes how many whole months have occurred between 1st January 2001 and #date. It then adds that number of months onto 1st February 2001. This expression should therefore always generate the 1st of the month that comes after #date. The second pair does the same but adds the computed number onto 1st March instead.
You should also note that I'm not applying any functions to _date, so if there happens to be a useful index on that column, it should be usable for this query.
This seems pretty simple,
Get Previous date of current date
SELECT DATEADD(MONTH,-1,GETDATE()) AS PrviousDate
Get Next date of current date
SELECT DATEADD(MONTH,1,GETDATE()) AS NextDate

Get date for nth day of week in nth week of month

I have a column with values like '3rd-Wednesday', '2nd-Tuesday', 'Every-Thursday'.
I'd like to create a column that reads those strings, and determines if that date has already come this month, and if it has, then return that date of next month. If it has not passed yet for this month, then it would return the date for this month.
Expected results (on 4/22/16) from the above would be: '05-18-2016', '05-10-2016', '04-28-2016'.
I'd prefer to do it mathematically and avoid creating a calendar table if possible.
Thanks.
Partial answer, which is by no means bug free.
This doesn't cater for 'Every-' entries, but hopefully will give you some inspiration. I'm sure there are plenty of test cases this will fail on, and you might be better off writing a stored proc.
I did try to do this by calculating the day name and day number of the first day of the month, then calculating the next wanted day and applying an offset, but it got messy. I know you said no date table but the CTE simplifies things.
How it works
A CTE creates a calendar for the current month of date and dayname. Some rather suspect parsing code pulls the day name from the test data and joins to the CTE. The where clause filters to dates greater than the Nth occurrence, and the select adds 4 weeks if the date has passed. Or at least that's the theory :)
I'm using DATEFROMPARTS to simplify the code, which is a SQL 2012 function - there are alternatives on SO for 2008.
SELECT * INTO #TEST FROM (VALUES ('3rd-Wednesday'), ('2nd-Tuesday'), ('4th-Monday')) A(Value)
SET DATEFIRST 1
;WITH DAYS AS (
SELECT
CAST(DATEADD(MONTH,DATEDIFF(MONTH,0,GETDATE()),N.Number) AS DATE) Date,
DATENAME(WEEKDAY, DATEADD(MONTH,DATEDIFF(MONTH,0,GETDATE()),N.Number)) DayName
FROM master..spt_values N WHERE N.type = 'P' AND N.number BETWEEN 0 AND 31
)
SELECT
T.Value,
CASE WHEN MIN(D.Date) < GETDATE() THEN DATEADD(WEEK, 4, MIN(D.DATE)) ELSE MIN(D.DATE) END Date
FROM #TEST T
JOIN DAYS D ON REVERSE(SUBSTRING(REVERSE(T.VALUE), 1, CHARINDEX('-', REVERSE(T.VALUE)) -1)) = D.DayName
WHERE D.Date >=
DATEFROMPARTS(
YEAR(GETDATE()),
MONTH(GETDATE()),
1+ 7*(CAST(SUBSTRING(T.Value, 1,1) AS INT) -1)
)
GROUP BY T.Value
Value Date
------------- ----------
2nd-Tuesday 2016-05-10
3rd-Wednesday 2016-05-18
4th-Monday 2016-04-25

How does this aggregated query find the correct values

I am having trouble understanding this code. It actually seems to work, but I don't understand how the correct value for activity year and month is "found" get the proper min and max? Or is it running all permutations getting the highest? This is very strange to me.
I do understand how the dateadd works, just not how the query is actually working on the whole. This may be a bad question since I don't actually need help solving a problem, just insight into why this works.
select
EmployeeNumber,
sum(BaseCalculation) as BaseCalculation,
min(dateadd(mm, (ActivityYear - 1900) * 12 + ActivityMonth - 1 , 0)) as StartDate,
max(dateadd(mm, (ActivityYear - 1900) * 12 + ActivityMonth - 1 , 0)) as EndDate
from
Compensation
where
1=1
-- and
group by
EmployeeNumber
for both the min and max function call, the algorithm is
dateadd(mm, (ActivityYear - 1900) * 12 + ActivityMonth - 1 , 0)
Your query compute all possible date from the Compensation table using this algorithm. Then, you select the minimum date as StartDate and the maximum as EndDate.
This is how the proper max and min are returned.
Note that the dateadd signature is DATEADD (datepart , number , date )
Since the last parameter is 0, you are addind to month(mm) the number calculated in the algorithm, and return the corresponding date starting from 0.
Check this out for more information : https://msdn.microsoft.com/en-us/library/ms186819.aspx
It is converting the columns ActivityYear and ActivityMmonth to a date. It is doing so by counting the number of months since 1900 and adding them to time zero. So, Jan 2000 would become something like Jan, 100. This seems like a very arcane calculation, because dates that are about 2,000 years old are not really useful.
Of course, this assumes that ActivityYear is a recognizable recent year.
I would convert the year and month to the first day of the beginning of the month, with something like this:
min(cast(cast(ActivityYear * 10000 + ActivityMonth + 1 as varchar(255)) as date)
Sql Server will calculate every value of that statement, and then only return the min and max.
Although I cannot say for sure that sql server executes this way internally, the way I think about it I imagine that the engine strips off the group by and all the aggregate functions, runs that query. And then just sums/finds the min etc off of that.

MS Access SQL query for testing dates for last day of the quarter

OK, so I have a load of records in a table and they have many different dates.
I want to retern only those records whose date falls on the last day of whatever quarter it's in.
I.e. I basically need the equivalent of a lastDayOfQuarter(date) function that calculates the date that is the last day in the quarter for the date passed to it.
e.g. lastDayOfQuarter(#16/05/2013#) = #30/06/2013#
My query might look like:
SELECT * FROM mytable
WHERE mytable.rdate = lastDayOfQuarter(mytable.rdate);
This query will be run over PDO so no VBA allowed. Native MS Access only.
I would also prefer to not use string manipulation as there is a difference between US and EU dates which might cause issues down the line.
I'm answering myself as, with the help of HansUp answering a previous question of mine for finding month-end records, I found out quite an easy way to acheive this:
WHERE DateValue(m.rdate) = DateSerial(Year(m.rdate), Month(m.rdate) + 1, 0)
AND Month(m.rdate) IN(3,6,9,12)
the "last day of the quarter" could be different for different users. You may be best to build a table of "lastdays" based on your business rules, then use that table in your query.
And here as short answer...Try
Select DateAdd(day, -1, dateadd(qq, DATEDIFF(qq, 0, 'year-month-day'), 0))
For today it should give you
2013-06-30 00:00:00.000
SO for your Table you should use :
SELECT * FROM mytable
WHERE mytable.rdate = DateAdd(day, -1, dateadd(qq, DATEDIFF(qq, 0, mytable.rdate), 0));

SQL server - Select all items with a date on the previous month

I have a table in my SQL Server database called "items" which has a column called "dateFinished".
I have a script that will run on the 1st day of each month which needs to select all items that finished in the previous month.
So, for example, on the 1st February it will need to select all items where the dateFinished is greater than or equal to 00:00 on the 1st of January and less than 00:00 on 1st February.
it also needs to work across new years (e.g. DEC - JAN).
Any ideas?
Select *
from items
where datefinished >= dateadd(m, datediff(m, 0, GETDATE()) - 1, 0)
AND datefinished < dateadd(m, datediff(m, 0, GETDATE()), 0)
You could get a day of the previous month with dateadd(m,-1,getdate()). Then, filter on the year and month of that date in a where clause, like:
select *
from items
where datepart(yy,dateFinished) = datepart(yy,dateadd(m,-1,getdate()))
and datepart(m,dateFinished) = datepart(m,dateadd(m,-1,getdate()))
This should work across years, and also if the query is run on a later day than the first of the month.
Simple just use what I just used: DATEDIFF(mm,dateFinished,GETDATE()) = 1
SELECT *
FROM items
WHERE DATEDIFF(mm,dateFinished,GETDATE()) = 1
I would start by checking out the DATEADD function
http://msdn.microsoft.com/en-us/library/ms186819.aspx