Get first date of each month SQL Server 2008 R2 - sql

I need to generate first date of each month starting 01/01/2000 to 12/31/2099.
Issue: Below query works in Management Studio. However, it doesn't work in SSRS. Unfortunately, I cannot create a function or a view because I do not have access to do so.
Is there an alternative way to write this query that would work with SSRS? SSRS does not like use of "TOP" in my query.
Error related to "TOP":
A TOP or FETCH clause contains an invalid value.
declare #pStartDate date = '01/01/2000'
declare #pEndDate date = '12/31/2099'
;With months(DATE, MONTH, YEAR)
as(
SELECT TOP (DATEDIFF(MONTH, #pStartDate, #pEndDate)+1)
TheDate = DATEADD(MONTH, number, #pStartDate),
TheMonth = MONTH(DATEADD(MONTH, number, #pStartDate)),
TheYear = YEAR(DATEADD(MONTH, number, #pStartDate))
FROM [master].dbo.spt_values
WHERE [type] = N'P' ORDER BY number
)
select cast(DATE as Date) as Date from months

I just rearranged your code in a simpler and efficient way, without TOP clause:
declare #pStartDate date = '01/01/2000'
declare #pEndDate date = '12/31/2099'
SELECT DATEADD(MONTH, number, #pStartDate) as Date
FROM [master].dbo.spt_values
WHERE [type] = N'P'
AND number < DATEDIFF(MONTH, #pStartDate, #pEndDate)+1
ORDER BY number
When you add one month to a date it keeps the day. Day 30 and 31 are special cases, try it yourself.

Try this simple, recursive CTE to get what you want:
declare #pStartDate date = '01/01/2000'
declare #pEndDate date = '12/31/2099'
;with FirstDayOfMonth as(
select #pStartDate as [firstDayOfMonth]
union all
select DATEADD(month, 1, [firstDayOfMonth]) from FirstDayOfMonth
where DATEADD(month, 1, [firstDayOfMonth]) < #pEndDate
)
select * from FirstDayOfMonth
option (maxrecursion 0)

Related

compute start date of sub-date-range from given main date range in sql server

input is a main date range which is stored in myTbl.StartDate and myTbl.EndDate columns values.
for a given constant value as divider, I need to compute start date of sub-date-ranges gained from dividing main date range to sub-date-ranges with constant value length.
examples are best explanations:
for main date range between '2017-1-1' to '2017-1-22' and const_val = 6 output is:
2017-1-1
2017-1-7 (1 + 6)
2017-1-13 (7 + 6)
2017-1-19 (13 + 6)
its noticable that the SQL user is not administrator(I can't use system tables)
You can use a recursive cte to do this:
DECLARE #startDate DATE = '20170101' ,
#endDate DATE = '20170122' ,
#Const_val INT = 6;
WITH cte
AS ( SELECT #startDate AS DateVal
UNION ALL
SELECT DATEADD(DAY, #Const_val, cte.DateVal)
FROM cte
WHERE DATEADD(DAY, #Const_val, cte.DateVal) <= #endDate
)
SELECT *
FROM cte;
Produces:
DateVal
==========
2017-01-01
2017-01-07
2017-01-13
2017-01-19
The first part of the cte gets the first date:
SELECT #startDate AS DateVal
Then it adds 6 days with the UNION ALL until the WHERE condition is met.
You can try the following:
DECLARE #StartDate DATE = CONVERT(date, '2017-01-01');
DECLARE #EndDate DATE = CONVERT(date, '2017-01-22');
DECLARE #FixDateDiff int = 6;
WITH cte AS (
SELECT #StartDate AS MyDate
UNION ALL
SELECT DATEADD(d, 1, MyDate)
FROM cte
WHERE DATEADD(d, 1, MyDate) <= CONVERT(date, #EndDate)
)
SELECT MyDate
FROM cte
WHERE DATEDIFF(d, #StartDate, MyDate)%#FixDateDiff = 0
However, instead of the fixed dates I assigned to the variables, simply replace the variables with your StartDate and EndDate column and extend the cte accordingly.

Showing dates by day wise, and having default date based on condition

am attaching a query to bring dates to a field,
if system time is before 01:00 pm then it should bring today date+1 else if system date is after or equal 01:00 pm it should bring today date +2
further , user can see the query result that will show him a date range along side with day names , and select one of them manually if the above cases wasn't what the user is looking for .
queries are avilable and working fine but separately,
one query bring date range , and one query set the date based on a condition , i need to make them one query , that bring the date ranges and set default date to be based on the conditions;
here are the queries :
Note : Q No 2 , I tried to make both queries as one query but its not bring the correct result , it keep bring today date +1 no matter what system time is.
1)
If DATEPART(Hour,Getdate())<12
SELECT GetDate()+1
If DATEPART(Hour,Getdate())>=12
SELECT GetDate()+2
2)
If DATEPART(Hour,Getdate())<12
DECLARE #Date1 DATE, #Date2 DATE
SET #Date1 = GetDate()+0
SET #Date2 = GetDate()+365
SELECT DATEADD(DAY,number+1,#Date1) [Date], DateName(Weekday,DATEADD(DAY,number+1,#Date1)) as dayname
FROM master..spt_values
WHERE type = 'P'
AND DATEADD(DAY,number+1,#Date1) < #Date2
If DATEPART(Hour,Getdate())>=12
DECLARE #Date3 DATE, #Date4 DATE
SET #Date3 = GetDate()+2
SET #Date4 = GetDate()+365
SELECT DATEADD(DAY,number+1,#Date3) [Date], DateName(Weekday,DATEADD(DAY,number+1,#Date3)) as dayname
FROM master..spt_values
WHERE type = 'P'
AND DATEADD(DAY,number+1,#Date3) < #Date4
I've used a CTE to calculate the start date. The CTE uses a CASE statement to tell if it is being run before or after 12.
WITH Base AS
(
/* Returns the start date based on the current time.
* Before midday the start date is today.
* After midday it is the day after tomorrow.
*/
SELECT
CASE
-- Use current time to calculate start date.
WHEN DATEPART(HOUR, GETDATE()) < 12 THEN DATEADD(DAY, 0, GETDATE())
ELSE DATEADD(DAY, 2, GETDATE())
END AS StartDate
)
SELECT
DATEADD(DAY, sv.Number + 1, b.StartDate) AS [Date],
DATENAME(WEEKDAY, DATEADD(DAY, sv.Number + 1, b.StartDate)) AS [DateName]
FROM
master..spt_values AS sv
CROSS JOIN Base AS b
WHERE
sv.[type] = 'P'
AND DATEADD(DAY, Number + 1, b.StartDate) < DATEADD(YEAR, 1, b.StartDate)
;

Calculate last days of months for given period in SQL Server

Is it possible to do in SQL: for example I have period where #s_date = '20130101' and #e_date = '20130601' and I want to select all last days of months in this period.
This is example of result:
20130131
20130228
20130331
20130430
20130531
Thanks.
The easiest option is to have a calendar table, with a last day of the month flag, so your query would simply be:
SELECT *
FROM dbo.Calendar
WHERE Date >= #StartDate
AND Date <= #EndDate
AND EndOfMonth = 1;
Assuming of course that you don't have a calendar table you can generate a list of dates on the fly:'
DECLARE #s_date DATE = '20130101',
#e_date DATE = '20130601';
SELECT Date = DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY Object_ID) - 1, #s_date)
FROM sys.all_objects;
Then once you have your dates you can limit them to where the date is the last day of the month (where adding one day makes it the first of the month):
DECLARE #s_date DATE = '20130101',
#e_date DATE = '20130601';
WITH Dates AS
( SELECT Date = DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY Object_ID) - 1, #s_date)
FROM sys.all_objects
)
SELECT *
FROM Dates
WHERE Date <= #e_Date
AND DATEPART(DAY, DATEADD(DAY, 1, Date)) = 1;
Example on SQL Fiddle
You can run the following query and then adjust it by using your table details:
declare #s_date as datetime= '20130101'
declare #e_date as datetime= '20131020'
SELECT DateAdd(m, number, '1990-01-31')
FROM master.dbo.spt_values
WHERE 'P' = type
AND DateAdd(m, number, #s_date) < #e_date
example for 20130101 :
select CONVERT(VARCHAR(8),
dateadd(day, -1, dateadd(month, 1,
convert(datetime, '20130101',112))), 112)
result :
20130131
Try this query
WITH sample
AS (SELECT Cast('2013-04-01' AS DATETIME) Date
UNION ALL
SELECT Dateadd(day, 1, date) dt
FROM sample
WHERE date < Cast('2013-05-05' AS DATETIME))
SELECT *
FROM sample
Fiddle
EOMONTH(#date) is the function you need.
Here is the help page https://learn.microsoft.com/en-us/sql/t-sql/functions/eomonth-transact-sql?view=sql-server-2017
This query gets the las 50 End Of Months.
The original query used as an example is from here.
https://dba.stackexchange.com/a/186829
WITH cte AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 1)) - 1 AS [Incrementor]
FROM [master].[sys].[columns] sc1
CROSS JOIN [master].[sys].[columns] sc2
)
SELECT top 50 EOMONTH(DATEADD(Month, -1 * cte.[Incrementor], GETDATE()))
FROM cte
WHERE EOMONTH(DATEADD(Month, -1 * cte.[Incrementor], GETDATE())) < GETDATE();

Group days by week

Is there is a way to group dates by week of month in SQL Server?
For example
Week 2: 05/07/2012 - 05/13/2012
Week 3: 05/14/2012 - 05/20/2012
but with Sql server statement
I tried
SELECT SOMETHING,
datediff(wk, convert(varchar(6), getdate(), 112) + '01', getdate()) + 1 AS TIME_
FROM STATISTICS_
GROUP BY something, TIME_
ORDER BY TIME_
but it returns the week number of month. (means 3)
How to get the pair of days for current week ?
For example, now we are in third (3rd) week and I want to show 05/14/2012 - 05/20/2012
I solved somehow:
SELECT DATEADD(ww, DATEDIFF(ww,0,<my_column_name>), 0)
select DATEADD(ww, DATEDIFF(ww,0,<my_column_name>), 0)+6
Then I will get two days and I will concatenate them later.
All right, bear with me here. We're going to build a temporary calendar table that represents this month, including the days from before and after the month that fall into your definition of a week (Monday - Sunday). I do this in a lot of steps to try to make the process clear, but I probably haven't excelled at that in this case.
We can then generate the ranges for the different weeks, and you can join against your other tables using that.
SET DATEFIRST 7;
SET NOCOUNT ON;
DECLARE #today SMALLDATETIME, #fd SMALLDATETIME, #rc INT;
SELECT #today = DATEADD(DAY, DATEDIFF(DAY, 0, GETDATE()), 0), -- today
#fd = DATEADD(DAY, 1-DAY(#today), #today), -- first day of this month
#rc = DATEPART(DAY, DATEADD(DAY, -1, DATEADD(MONTH, 1, #fd)));-- days in month
DECLARE #thismonth TABLE (
[date] SMALLDATETIME,
[weekday] TINYINT,
[weeknumber] TINYINT
);
;WITH n(d) AS (
SELECT TOP (#rc+12) DATEADD(DAY, ROW_NUMBER() OVER
(ORDER BY [object_id]) - 7, #fd) FROM sys.all_objects
)
INSERT #thismonth([date], [weekday]) SELECT d, DATEPART(WEEKDAY, d) FROM n;
DELETE #thismonth WHERE [date] < (SELECT MIN([date]) FROM #thismonth WHERE [weekday] = 2)
OR [date] > (SELECT MAX([date]) FROM #thismonth WHERE [weekday] = 1);
;WITH x AS ( SELECT [date], weeknumber, rn = ((ROW_NUMBER() OVER
(ORDER BY [date])-1) / 7) + 1 FROM #thismonth ) UPDATE x SET weeknumber = rn;
-- now, the final query given all that (I've only broken this up to get rid of the vertical scrollbars):
;WITH ranges(w,s,e) AS (
SELECT weeknumber, MIN([date]), MAX([date]) FROM #thismonth GROUP BY weeknumber
)
SELECT [week] = CONVERT(CHAR(10), r.s, 120) + ' - ' + CONVERT(CHAR(10), r.e, 120)
--, SOMETHING , other columns from STATISTICS_?
FROM ranges AS r
-- LEFT OUTER JOIN dbo.STATISTICS_ AS s
-- ON s.TIME_ >= r.s AND s.TIME_ < DATEADD(DAY, 1, r.e)
-- comment this out if you want all the weeks from this month:
WHERE w = (SELECT weeknumber FROM #thismonth WHERE [date] = #today)
GROUP BY r.s, r.e --, SOMETHING
ORDER BY [week];
Results with WHERE clause:
week
-----------------------
2012-05-14 - 2012-05-20
Results without WHERE clause:
week
-----------------------
2012-04-30 - 2012-05-06
2012-05-07 - 2012-05-13
2012-05-14 - 2012-05-20
2012-05-21 - 2012-05-27
2012-05-28 - 2012-06-03
Note that I chose YYYY-MM-DD on purpose. You should avoid regional formatting like M/D/Y especially for input but also for display. No matter how targeted you think your audience is, you're always going to have someone who thinks 05/07/2012 is July 5th, not May 7th. With YYYY-MM-DD there is no ambiguity whatsoever.
Create a calendar table, then you can query week numbers, first/last days of specific weeks and months etc. You can also join on it queries to get a date range etc.
How about a case statement?
case when datepart(day, mydatetime) between 1 and 7 then 1
when datepart(day, mydatetime) between 8 and 14 then 2
...
You'll also have to include the year & month unless you want all the week 1s in the same group.
It's not clear of you want to "group dates by week of month", or alternately "select data from a given week"
If you mean "group" this little snippet should get you 'week of month':
SELECT <stuff>
FROM CP_STATISTICS
WHERE Month(<YOUR DATE COL>) = 5 --april
GROUP BY Year(<YOUR DATE COL>),
Month(<YOUR DATE COL>),
DATEDIFF(week, DATEADD(MONTH, DATEDIFF(MONTH, 0, <YOUR DATE COL>), 0)
, <YOUR DATE COL>) +1
Alternately, if you want "sales for week 1 of April, ordered by date" You could do something like..
DECLARE #targetDate datetime2 = '5/3/2012'
DECLARE #targetWeek int = DATEDIFF(week, DATEADD(MONTH,
DATEDIFF(MONTH, 0, #targetDate), 0), #targetDate) +1
SELECT <stuff>
FROM CP_STATISTICS
WHERE MONTH(#targetDate) = Month(myDateCol) AND
YEAR(#targetDate) = Year (myDateCol) AND
#targetWeek = DATEDIFF(week, DATEADD(MONTH,
DATEDIFF(MONTH, 0, myDateCol), 0), myDateCol) +1
ORDER BY myDateCol
Note, things would get more complicated if you use non-standard weeks, or want to reach a few days into an earlier month for weeks that straddle a month boundary.
EDIT 2
From looking at your 'solved now' section. I think your question is "how do I get data out of a table for a given week?"
Your solution appears to be:
DECLARE #targetDate datetime2 = '5/1/2012'
DECLARE #startDate datetime2 = DATEADD(ww, DATEDIFF(ww,0,targetDate), 0)
DECLARE #endDate datetime2 = DATEADD(ww, DATEDIFF(ww,0,#now), 0)+6
SELECT <stuff>
FROM STATISTICS_
WHERE dateStamp >= #startDate AND dateStamp <= #endDate
Notice how if the date is 5/1 this solution results in a start date of '4/30/2012'. I point this out because your solution crosses month boundaries. This may or may not be desirable.

Getting Number of weeks in a Month from a Datetime Column

I have a table called FcData and the data looks like:
Op_Date
2011-02-14 11:53:40.000
2011-02-17 16:02:19.000
2010-02-14 12:53:40.000
2010-02-17 14:02:19.000
I am looking to get the Number of weeks in That Month from Op_Date. So I am looking for output like:
Op_Date Number of Weeks
2011-02-14 11:53:40.000 5
2011-02-17 16:02:19.000 5
2010-02-14 12:53:40.000 5
2010-02-17 14:02:19.000 5
This page has some good functions to figure out the last day of any given month: http://www.sql-server-helper.com/functions/get-last-day-of-month.aspx
Just wrap the output of that function with a DATEPART(wk, last_day_of_month) call. Combining it with an equivalent call for the 1st-day-of-week will let you get the number of weeks in that month.
Use this to get the number of week for ONE specific date. Replace GetDate() by your date:
declare #dt date = cast(GetDate() as date);
declare #dtstart date = DATEADD(day, -DATEPART(day, #dt) + 1, #dt);
declare #dtend date = dateadd(DAY, -1, DATEADD(MONTH, 1, #dtstart));
WITH dates AS (
SELECT #dtstart ADate
UNION ALL
SELECT DATEADD(day, 1, t.ADate)
FROM dates t
WHERE DATEADD(day, 1, t.ADate) <= #dtend
)
SELECT top 1 DatePart(WEEKDAY, ADate) weekday, COUNT(*) weeks
FROM dates d
group by DatePart(WEEKDAY, ADate)
order by 2 desc
Explained: the CTE creates a result set with all dates for the month of the given date. Then we query the result set, grouping by week day and count the number of occurrences. The max number will give us how many weeks the month overlaps (premise: if the month has 5 Mondays, it will cover five weeks of the year).
Update
Now, if you have multiple dates, you should tweak accordingly, joining your query with the dates CTE.
Here is my take on it, might have missed something.
In Linq:
from u in TblUsers
let date = u.CreateDate.Value
let firstDay = new DateTime(date.Year, date.Month, 1)
let lastDay = firstDay.AddMonths(1)
where u.CreateDate.HasValue
select Math.Ceiling((lastDay - firstDay).TotalDays / 7)
And generated SQL:
-- Region Parameters
DECLARE #p0 Int = 1
DECLARE #p1 Int = 1
DECLARE #p2 Float = 7
-- EndRegion
SELECT CEILING(((CONVERT(Float,CONVERT(BigInt,(((CONVERT(BigInt,DATEDIFF(DAY, [t3].[value], [t3].[value2]))) * 86400000) + DATEDIFF(MILLISECOND, DATEADD(DAY, DATEDIFF(DAY, [t3].[value], [t3].[value2]), [t3].[value]), [t3].[value2])) * 10000))) / 864000000000) / #p2) AS [value]
FROM (
SELECT [t2].[createDate], [t2].[value], DATEADD(MONTH, #p1, [t2].[value]) AS [value2]
FROM (
SELECT [t1].[createDate], CONVERT(DATETIME, CONVERT(NCHAR(2), DATEPART(Month, [t1].[value])) + ('/' + (CONVERT(NCHAR(2), #p0) + ('/' + CONVERT(NCHAR(4), DATEPART(Year, [t1].[value]))))), 101) AS [value]
FROM (
SELECT [t0].[createDate], [t0].[createDate] AS [value]
FROM [tblUser] AS [t0]
) AS [t1]
) AS [t2]
) AS [t3]
WHERE [t3].[createDate] IS NOT NULL
According to this MSDN article: http://msdn.microsoft.com/en-us/library/ms174420.aspx you can only get the current week in the year, not what that month returns.
There may be various approaches to implementing the idea suggested by #Marc B. Here's one, where no UDFs are used but the first and the last days of month are calculated directly:
WITH SampleData AS (
SELECT CAST('20110214' AS datetime) AS Op_Date
UNION ALL SELECT '20110217'
UNION ALL SELECT '20100214'
UNION ALL SELECT '20100217'
UNION ALL SELECT '20090214'
UNION ALL SELECT '20090217'
),
MonthStarts AS (
SELECT
Op_Date,
MonthStart = DATEADD(DAY, 1 - DAY(Op_Date), Op_Date)
/* alternatively: DATEADD(MONTH, DATEDIFF(MONTH, 0, Op_Date), 0) */
FROM FcData
),
Months AS (
SELECT
Op_Date,
MonthStart,
MonthEnd = DATEADD(DAY, -1, DATEADD(MONTH, 1, MonthStart))
FROM FcData
)
Weeks AS (
SELECT
Op_Date,
StartWeek = DATEPART(WEEK, MonthStart),
EndWeek = DATEPART(WEEK, MonthEnd)
FROM MonthStarts
)
SELECT
Op_Date,
NumberOfWeeks = EndWeek - StartWeek + 1
FROM Weeks
All calculations could be done in one SELECT, but I chose to split them into steps and place every step in a separate CTE so it could be seen better how the end result was obtained.
You can get number of weeks per month using the following method.
Datepart(WEEK,
DATEADD(DAY,
-1,
DATEADD(MONTH,
1,
DATEADD(DAY,
1 - DAY(GETDATE()),
GETDATE())))
-
DATEADD(DAY,
1 - DAY(GETDATE()),
GETDATE())
+1
)
Here how you can get accurate amount of weeks:
DECLARE #date DATETIME
SET #date = GETDATE()
SELECT ROUND(cast(datediff(day, dateadd(day, 1-day(#date), #date), dateadd(month, 1, dateadd(day, 1-day(#date), #date))) AS FLOAT) / 7, 2)
With this code for Sep 2014 you'll get 4.29 which is actually true since there're 4 full weeks and 2 more days.