In TSQL I can do this to get the amount of days in some month:
declare #date as datetime
set #date = '2015-02-06'
select datediff(day, dateadd(day, 1-day(#date), #date),
dateadd(month, 1, dateadd(day, 1-day(#date), #date)))
How can I get the same functionality in MDX?
P.S. I need the result to be an int.
If you have a year-month-date hierarchy in place, it could be done in the below fashion:
WITH MEMBER NumOfDaysInMonth AS
DateDiff(
"d",
HEAD(DESCENDANTS([Date].[Calendar Date].CURRENTMEMBER, 1)).ITEM(0).MEMBER_CAPTION, //Gets the first date of the month
TAIL(DESCENDANTS([Date].[Calendar Date].CURRENTMEMBER, 1)).ITEM(0).MEMBER_CAPTION //Gets the last date of the month
) + 1
You just need to to pass the month's value in the slicer. The calculated member will do the rest.
SELECT NumOfMonths ON 0
FROM [YourCube]
WHERE ([Date].[Calendar Date].[Month].&[Dec-2015])
This is the method we use:
WITH
MEMBER [MEASURES].[NumOfDaysInMonth] AS
IIF
(
VBA!Isdate([Date].[Calendar].CurrentMember.Name)
,Datepart
("D"
,
Dateadd
("M"
,1
,Cdate
(
Cstr(VBA!Month([Date].[Calendar].CurrentMember.Name)) + "-01-"
+
Cstr(VBA!Year([Date].[Calendar].CurrentMember.Name))
)
)
- 1
)
,''
)
SELECT
NON EMPTY
{[MEASURES].[NumOfDaysInMonth]} ON 0
,NON EMPTY
{
[Date].[Calendar].[All]
,[Date].[Calendar].[Calendar Year].&[2005]
,[Date].[Calendar].[Calendar Semester].&[2008]&[2]
,[Date].[Calendar].[Month].&[2006]&[3]
,[Date].[Calendar].[Date].&[20060214]
,[Date].[Calendar].[Month].&[2007]&[11]
} ON 1
FROM [Adventure Works];
The above returns the following:
Related
I have been asked to create two datasets showing 7 days of dates from a two date range.
Example: I have a date range of StartDate = 2022-12-12 and EndDate = 2022-12-25. I need a query to display the individual dates in between these two dates. I was told to use DATEADD, but cannot for the life figure this out.
Any help would be be helpful, thank you.
SELECT DATEADD(DAY, 7, StartDate) AS WeekOne
I was expecting something like this:
2022-12-12
2022-12-13
2022-12-14
2022-12-15
2022-12-16
2022-12-17
2022-12-18
DECLARE #InStartDate DATE='2022-12-12';
DECLARE #InStopDate DATE='2022-12-25';
WITH GEN AS
(
SELECT #InStartDate AS Start_dated
UNION ALL
SELECT DATEADD(DD,1,G.Start_dated)
FROM GEN AS G
WHERE G.Start_dated< #InStopDate
)
SELECT G.*
FROM GEN AS G
You can use something like this
You need to start by generating a numbers table. It needs enough rows to handle each day between your start and end dates. Something like this:
with Numbers as (
select 0 as n
union all
select n + 1
from Numbers
where n < 365
)
select n
from Numbers
option(maxrecursion 0);
Given the example range, I felt like 365 days (one year) was adequate, but it's easy to tweak that range (as we'll see).
Once you have the Numbers table, you can use DateAdd() to add that amount to the start date:
DECLARE #StartDate date = '20221212';
with Numbers as (
select 0 as n
union all
select n + 1
from Numbers
where n < 365
)
select DATEADD(day, n, #StartDate)
from Numbers
option(maxrecursion 0)
From here it's a simple matter to use the EndDate in a WHERE clause to limit the total rows:
DECLARE #StartDate date = '20221212';
DECLARE #EndDate date = '20221231';
with Numbers as (
select 0 as n
union all
select n + 1
from Numbers
where n < DATEDIFF(day, #StartDate, #EndDate)
)
select DATEADD(day, n, #StartDate)
from Numbers
option(maxrecursion 0)
For SQL Server 2022 and later you can use Generate_Series:
declare #StartDate as Date = '20221212', #EndDate as Date = '20221225';
select DateAdd( day, Value, #StartDate ) as TargetDate
from Generate_Series( 0, DateDiff( day, #StartDate, #EndDate ) );
dbfiddle.
AND (
(datediff(DAY,getdate(), '2020-07-14 00:00:00.000') = 5)
OR (
datediff(DAY,getdate(), A.HX_RELIEVING_DT) BETWEEN 0 AND 4
AND '2020-04-17 20:36:53.000' >= GETDATE()-1
)
)
This is the output I want for relieving date and last updated time but I am unable to get this
EMPLID LOCATION SUPERVISOR_ID HX_RELIEVING_DT LASTUPDDTTM
-- SINGAPORE --- 2020-07-14 00:00:00.000 2020-04-17 20:36:53.000
I believe you tried to write if #MyDate is past:
AND ((DATEDIFF(DAY, #MyDate, GETDATE()) = 5) OR (DATEDIFF( DAY, A.HX_RELIEVING_DT, GETDATE()) BETWEEN 0 AND 4 AND DATEADD(DAY, -1, GETDATE()) <= #MyDate))
If #MyDate is future:
AND ((DATEDIFF(DAY, GETDATE(), #MyDate) = 5) OR (DATEDIFF(DAY, GETDATE(), A.HX_RELIEVING_DT) BETWEEN 0 AND 4 AND DATEADD(DAY, 1, GETDATE()) <= #MyDate))
Last condition from your WHERE clause is basically filtering your record from the output.
AND '2020-04-17 20:36:53.000' >= GETDATE()-1
A date from month "April" can not be bigger than a date value from July, right? You can check the values you are using in different condition in WHERE clause with this below query. You will get your issue automatically.
SELECT *,
datediff(DAY,getdate(), '2020-07-14 00:00:00.000'),
datediff(DAY,getdate(), A.HX_RELIEVING_DT),
GETDATE()-1
FROM your_table A
If a user selects a range such as:
Start: November 2016
End: September 2017,
I want to include all results that fall within the range of 2016-11-01 to 2017-09-30.
I tried concatenating together the year, month, and day, however the issue comes that not all months have the same last day. While I know all months start on day 01, a month's end day can be 28, 29, 30, or 31.
Is there a way to do this without constructing the date? SqlServer 2008 doesn't have the EOMONTH function, and I feel like anything more complex than that is not the right solution. I would like to avoid this:
WHERE
DateCol >= '2016' + '-' + '11' + '-01' AND
DateCol <= '2017' + '-' + '09' + '-30'
It really seems to me that the easiest and best answer is to go from the first of the beginning month to the first of the month after the ending month, and make the second comparison not inclusive.
In other words, instead of this:
WHERE
DateCol >= '2016' + '-' + '11' + '-01' AND
DateCol <= '2017' + '-' + '09' + '-30'
simply this:
WHERE
DateCol >= '2016' + '-' + '11' + '-01' AND
DateCol < '2017' + '-' + '10' + '-01'
There is a faster way to do so :
DECLARE #minDate DATE
DECLARE #maxDate DATE
SET #minDate = XXXXX
SET #maxDate = YYYYY
-- Get the first day of the month minDate.
SET #minDate = CONVERT(datetime,CONVERT(varchar(6),#minDate,112)+'01',112)
-- Get the last day of the month minDate.
SET #maxDate = CONVERT(datetime,CONVERT(varchar(6),#maxDate,112)+'01',112)
SET #maxDate = DATEADD(day, -1, DATEADD(month, 1, #maxDate))
SELECT * FROM myTABLE WHERE DateCol >= #minDate AND DateCol <= #maxDate
Or :
SELECT * FROM myTABLE
WHERE DateCol >= CONVERT(datetime,CONVERT(varchar(6),XXXXX,112)+'01',112)
AND DateCol <= DATEADD(day, -1, DATEADD(month, 1, CONVERT(datetime,CONVERT(varchar(6),YYYYY,112)+'01',112)))
Use syntax like CONVERT(datetime,'20170930',112) or CONVERT(datetime,'09-30-2017',110) for XXXXX and YYYYY rather than '2017-09-30' that use SQL Server implicit convertion from char to datetime (rely on the server configuration : can be hazardous!!!)).
Using this syntax is faster because #minDate and #maxDate do not need any evaluation. So that indexes can be used directly...
Otherwise a scalar function that will simulate the eomonth() behaviour could be usefull...
You could use following select statement to get last date of any month (and any year) by passing a field or date to it:
DECLARE #dtDate DATE
SET #dtDate = '09/25/2016'
SELECT CAST(DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,#dtDate)+1,0)) AS DATE) AS LastDay_AnyMonth
Please provide some example data and desired result and I will update my answer further.
Here you go:
DECLARE #YourTable TABLE (YourData DATE);
INSERT INTO #YourTable VALUES
('2016-11-01'),
('2016-09-05'),
('2017-03-03'),
('2017-11-11'),
('2017-12-14'),
('2017-09-30');
WITH CTE AS (
SELECT YourData
FROM #YourTable
WHERE YEAR(YourData) =2016 AND MONTH (YourData) >= 11
)
SELECT YourData
FROM #YourTable
WHERE YEAR(YourData) =2017 AND MONTH (YourData) <= 9
UNION ALL
SELECT YourData
FROM CTE;
There is no need to know the end of the month (28 or 30 or 31).
For 2008, you can simply convert the string
Example
Select Date1=convert(date,'November 2016')
,Date2=dateadd(DAY,-1,dateadd(MONTH,1,convert(date,'September 2017')))
Returns
Date1 Date2
2016-11-01 2017-09-30
So the WHERE would be somthing like this
...
Where DateCol between convert(date,'November 2016')
and dateadd(DAY,-1,dateadd(MONTH,1,convert(date,'September 2017')))
A useful construct for performing date-based operations is to make use of a Date Dimension table. You are creating a lookup table that is populated with a lot of information about dates over a large span of time. You can then query the table based on the information that you do have. The table is small enough so that it does not impose significant performance concerns.
In your particular case, you have the month and year. You would plug that into the date dimension table to get the first of the month from the beginning month and the last of the month from the ending month. You now have a time range to search over without any complex logic or calculations on the fly.
Aaron Bertrand explains it in depth here: https://www.mssqltips.com/sqlservertip/4054/creating-a-date-dimension-or-calendar-table-in-sql-server/
I have to get/create date from the user input of week of month (week number in that month - 1st,2nd,3rd,4th and last) and day of week (sunday,monday..) in SQL server.
Examples:
4th Sunday of every month, Last Friday of every month, First Monday etc.
I was able to do it easily in .net but SQL server does seem limited in the date functions.
I am having to use lot of logic to get the date. To calculate the date using the above two parameters I had to use lot of datepart function.
Any suggestions on how to come up with the optimal SQL query for such a function?
I created a function other day for another OP GET Month, Quarter based on Work Week number
This function takes the current year as default it can be further modified to take Year as a parameter too.
an extension to that function can produce the results you are looking for ....
WITH X AS
(
SELECT TOP (CASE WHEN YEAR(GETDATE()) % 4 = 0 THEN 366 ELSE 365 END)-- to handle leap year
DATEADD(DAY
,ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) -1
, CAST(YEAR(GETDATE()) AS VARCHAR(4)) + '0101' )
DayNumber
From master..spt_values
),DatesData AS(
SELECT DayNumber [Date]
,DATEPART(WEEKDAY,DayNumber) DayOfTheWeek
,DATEDIFF(WEEK,
DATEADD(WEEK,
DATEDIFF(WEEK, 0, DATEADD(MONTH,
DATEDIFF(MONTH, 0, DayNumber), 0)), 0)
, DayNumber- 1) + 1 WeekOfTheMonth
FROM X )
SELECT * FROM DatesData
WHERE DayOfTheWeek = 6 -- A function would expect these two parameters
AND WeekOfTheMonth = 4 -- #DayOfTheWeek and #WeekOfTheMonth
Here is a general formula:
declare #month as datetime --set to the first day of the month you wish to use
declare #week as int --1st, 2nd, 3rd...
declare #day as int --Day of the week (1=sunday, 2=monday...)
--Second monday in August 2015
set #month = '8/1/2015'
set #week = 2
set #day = 2
select dateadd(
day,
((7+#day) - datepart(weekday, #month)) % 7 + 7 * (#week-1),
#month
)
You can also find the last, 2nd to last... etc with this reverse formula:
--Second to last monday in August 2015
set #month = '8/1/2015'
set #week = 2
set #day = 2
select
dateadd(
day,
-((7+datepart(weekday, dateadd(month,1,#month)-1)-#day)) % 7 - 7 * (#week-1),
dateadd(month,1,#month)-1
)
I am trying to get the correct year based on the current date and append to the fiscal year months date part but I am getting an error that it is not an integer. Ideas or thoughts?
`ALTER FUNCTION [dbo].[fn_CS_IssuedMODs] (#currentDate DATE)
RETURNS TABLE
AS
RETURN
(SELECT cs.Specialist, CASE WHEN COUNT(mn.mod_number_id) IS NULL
THEN 0 ELSE COUNT(mn.mod_number_id) END AS IssuedMODS,
cs.user_certificateSerialNumber
FROM dbo.tbl_modificationNumbers AS mn RIGHT OUTER JOIN
dbo.vw_ContractSpecialists AS cs ON mn.mod_specialist_id = cs.user_certificateSerialNumber
WHERE (mn.statusID = 10) AND effective_date between '10/1/'+DATEPART(YEAR,#currentDate)
+ CASE WHEN DATEPART(MONTH, #CurrentDate) >= 10 THEN -1 ELSE 0 END AND '09/30/'+DATEPART (YEAR,#currentDate)
GROUP BY cs.Specialist, cs.user_certificateSerialNumber`
lets create some variables for testing:
DECLARE #currentDate DATETIME = '4/2/2014'
DECLARE #FiscalYearStart DATETIME
DECLARE #FiscalYearEnd DATETIME
Now we are going to check whether the current date is before or after October 1 and if so, we are going to start the fiscal year using the previous year, otherwise we are in the new fiscal year.
SELECT #FiscalYearStart =
(
CASE
WHEN DATEPART(MONTH, #currentDate) < 10 THEN
DATEADD(MONTH,9, DATEADD(YEAR, DATEDIFF(YEAR, 0, #currentDate) - 1, 0))
ELSE
DATEADD(MONTH,9, DATEADD(YEAR, DATEDIFF(YEAR, 0, #currentDate), 0))
END
),
#FiscalYearEnd =
(
CASE
WHEN DATEPART(MONTH, #currentDate) < 10 THEN
DATEADD(MONTH,9, DATEADD(YEAR, DATEDIFF(YEAR, 0, #currentDate), 0))
ELSE
DATEADD(MONTH,9, DATEADD(YEAR, DATEDIFF(YEAR, 0, #currentDate) + 1, 0))
END
)
SELECT #FiscalYearStart As FiscalYearStart, #FiscalYearEnd As FiscalYearEnd
Output:
FiscalYearStart FiscalYearEnd
2013-10-01 00:00:00.000 2014-10-01 00:00:00.000
Now you can use effective_date >= #FiscalYearStart AND effective_date < #FiscalYearEnd in your query to pull the correct data for the year.
Instead of datepart() use either datename(). Or, do an explicit cast() to a string.
The + operator is overloaded in SQL Server. When it encounters a number, it is treated as addition -- and you can't add strings.
I can't figure out what your code is supposed to be doing. There are probably better ways to accomplish what you want. For starters, you should use ISO standard date formats (YYYYMMDD or YYYY-MM-DD) for constants.
Try this:
ALTER FUNCTION [dbo].[fn_CS_IssuedMODs] (#currentDate DATE)
RETURNS TABLE
AS
RETURN
(SELECT cs.Specialist, CASE WHEN COUNT(mn.mod_number_id) IS NULL
THEN 0 ELSE COUNT(mn.mod_number_id) END AS IssuedMODS,
cs.user_certificateSerialNumber
FROM dbo.tbl_modificationNumbers AS mn RIGHT OUTER JOIN
dbo.vw_ContractSpecialists AS cs ON mn.mod_specialist_id = cs.user_certificateSerialNumber
WHERE (mn.statusID = 10) AND effective_date between Cast(DATEPART(YEAR, DateAdd(Year, CASE WHEN DATEPART(MONTH, #CurrentDate) >= 10 THEN -1 ELSE 0 END, #currentDate)) as varchar) + '-10-1'
AND Cast(DATEPART(YEAR,#currentDate) as varchar) + '-09-30'
GROUP BY cs.Specialist, cs.user_certificateSerialNumber