How do I format a date in a SQL Where clause? - tsql

I have a .NET 2010 app that bangs against a SQL db. On the app side, a user can search on Begin date and End Date. Bot of these are just Month + Year. I then format them so they are complete dates. So when they go to the stored proc they'll look like this...
Begin Date: 1/1/2011
End Date: 5/31/2011
But the date in the db is broken up into 3 int fields, Month,Day & Year, ...of which Day may or may not be filled in (0 if not). It would be ok for this to always default to one when running this query. So if the values in the db were Month=3, Day=0 Year=2011 I would like the sql statement to render as
Where FORMATTEDDATEHERE between '1/1/2011' and '5/31/2011'
I just can't figure out how to format sql fields in a where clause.

Did you try something like
WHERE CONVERT(DATETIME,
CONVERT(VARCHAR(4), [c_year]) + '/'
+ CONVERT(VARCHAR(2), [c_month]) + '/'
+ CONVERT(VARCHAR(2), [c_day]))
BETWEEN '2011/1/1' AND '2011/5/31'
Hope it helps

you could build the date using datadd functions within the WHERE clause EG.
SELECT ...
WHERE DATEADD(Day, field_days-1, DATEADD(Month, field_months-1, DATEADD(Year, field_years-1900, 0))) BETWEEN '1/1/2011' AND '5/31/2011'
Just replace field_days, field_months, field_years with the int fields on the table for days, months and year. This will return all records within the date range.
Is this what you require?

WHERE CAST(Year AS varchar) + RIGHT(100 + Month, 2) +
RIGHT(100 + COALESCE(NULLIF(Day, 0), 1), 2) BETWEEN ...

Related

Rewrite dynamic T-SQL date variables in DAX

We're currently rebuilding basic emailed reports built using T-SQL to be paginated reports published on Power BI.
We're muddling through by creating the tables we need with the appropriate filters in Power BI Desktop to reconcile the numbers, then taking the DAX code from them using the Performance Analyser.
The one I'm working at the minute has a simple bit of SQL code to get data for a previous calendar month. I have no idea how or if it's possible for this to exist in DAX?
-- Validation to get previous month
IF (MONTH(GETDATE()) - 1) > 0
SET #MONTH = MONTH(GETDATE()) - 1
ELSE
SET #MONTH = '12'
-- Validation to get year of previous month
IF (#MONTH < 12)
SET #YEAR = YEAR(GETDATE())
ELSE
SET #YEAR = YEAR(GETDATE()) - 1
-- Set start date and finish date for extract
SET #PERIOD = #YEAR + RIGHT('00' + #MONTH, 2)
It needs to become a hidden SSRS parameter or just inline code to be used with this DAX variable:
VAR __DS0FilterTable =
TREATAS({"202212"}, 'Org View_VaultexCalendar'[Calendar Month No])
So the "202212" would become #period or the equivalent if doable without a parameter.
SSRS Parameter:
=IIF(
Month(Today()) > 1,
Year(Today()) & RIGHT("00" & Month(Today()) - 1, 2),
Year(Today())-1 & "12"
)
DAX expression:
IF (
MONTH ( TODAY () ) > 1,
YEAR ( TODAY () ) & FORMAT ( MONTH ( TODAY () ) - 1, "00" ),
YEAR ( TODAY () ) - 1 & "12"
)
In both cases we look at today's month and check if it's after January. If it is, take the current year and concatenate it with the current month less one and padded with a leading zero when needed. In the other case we know that the month is January so take the current year less one and concatenate it with "12"

Transacting MS Access query (using IIF() and DATESERIAL()) into T-SQL

I'm trying to convert some MS Access queries to T-SQL to use in SSIS (basically converting Access db to SQL server 2008) and I'm having trouble converting an IIF() statement. I tried several approaches and it always resulted in an error.
The query creates a column with dates that are "original Date + 2 years if the condition is met and original Date + 1 if the condition is not met". The first part of the IIF() eliminates the case of the original year being a leap year and so the possibility of generating a non-existing date.
The original IIF() statement is:
IIf((Day(Date)=29 And Month(Date)=2),
IIf(Desc Like "*" & "123" & "*",
DateSerial(Year(Date)+2,Month(Date),Day(Date)-1),
DateSerial(Year(Date)+1,Month(Date),Day(Date)-1)),
IIf(Desc Like "*" & "123" & "*",
DateSerial(Year(Date)+2,Month(Date),Day(Date)),
DateSerial(Year(Date)+1,Month(Date),Day(Date)))) AS Term
So the problem is not only an IIF() statement but also DATESERIAL function. I found the solution for the DATESERIAL() function using CAST() (SQL server 2008 does not have the DATEFROMPARTS() function...).
I tried using CASE() like this:
CASE
WHEN DAY(Date)=29 AND Month(Date)=2 THEN
CASE
WHEN Desc LIKE "%123%" THEN
CAST(CAST(YEAR(Date)+2 AS VARCHAR(4)) + RIGHT('0' + CAST(MONTH(Date) AS VARCHAR(2)), 2) + RIGHT('0' + CAST(DAY(Date)-1 AS VARCHAR(2)), 2) AS DATETIME )
ELSE CAST(CAST(YEAR(Date)+1 AS VARCHAR(4)) + RIGHT('0' + CAST(MONTH(Date) AS VARCHAR(2)), 2) + RIGHT('0' + CAST(DAY(Date)-1 AS VARCHAR(2)), 2) AS DATETIME ) END
ELSE CASE
WHEN Desc LIKE "%123%" THEN
THEN CAST(CAST(YEAR(Date)+2 AS VARCHAR(4)) + RIGHT('0' + CAST(MONTH(Date) AS VARCHAR(2)), 2) + RIGHT('0' + CAST(DAY(Date) AS VARCHAR(2)), 2) AS DATETIME )
ELSE CAST(CAST(YEAR(Date)+1 AS VARCHAR(4)) + RIGHT('0' + CAST(MONTH(Date) AS VARCHAR(2)), 2) + RIGHT('0' + CAST(DAY(Date) AS VARCHAR(2)), 2) AS DATETIME )END END AS Term
I also tried using COAELSCE() but with no better outcome.
I really don't know if I have made some kind of syntax error or where the problem could be.
Thank you in advance for any help.
edit: I'll add error message I'm getting: Incorrect syntax near '...'. The '...' changes as I try different approaches, sometimes its ELSE, THEN etc.
SQL Server's DATEADD may help to simplify things here...
CASE WHEN (<your condition>)
THEN DATEADD(YEAR, 1, [OriginalDate])
ELSE DATEADD(YEAR, 2, [OriginalDate])
END
Should also cope with leap years too.
DateSerial is such a useful function to have around, just create your own.
I got my version from here:
CREATE FUNCTION dbo.DateSerial
(
#year int,
#month int,
#day int
)
RETURNS datetime
AS
BEGIN
DECLARE #date datetime
-- convert date by adding together like yyyymmdd
SET #date = cast(#year * 10000 + 101 AS char(8));
-- Add to date the proper months subtracting 1, since we used 1 as start instead of zero.
SET #date = dateadd(mm , #month - 1 , #date)
-- Add to date the proper days subtracting 1, since we used 1 as start instead of zero.
SET #date = dateadd(dd , #day - 1 , #date);
RETURN #date ;
END;
GO
Here is another way: Date serial in SQL?
Then use the function just as you did in Access.
Edit: just noticed that your outer IIF is to handle leap years. This is not needed when using dateadd().

MS Access : Dealing with today's date in calculated field expression

Hi I am creating a table in MS Access to store the details of children in a school.
I have a field called YearGroup which needs to calculate the school year they are in based on their date of birth and whether they have been moved up or down a year.
I.e. if the expression deems they are six years old they should be placed in year 2. If they were moved down or up a year they should be in year 1 or 3 (this is based on another field in the table called YearModifier).
The code I have at the moment is this:
Year(Now()) - IIf(Month([DOB]) > 8, Year([DOB]) + 6 + [YearModifier], Year([DOB]) + 5 + [YearModifier])
My problem is that Year(Now()) is returning as invalid expression. Lots of websites have recognised using the Now() function and also I've tried Date() but nothing seems to be accepted by Access (The version is 2010).
What is going on? How can I get today's date in a calculated field expression?
Thanks
Try creating a query with all of the fields from your table, and then add an extra field YearGroup: Year(Now()) - IIf(Month([DOB]) > 8, Year([DOB]) + 6 + [YearModifier], Year([DOB]) + 5 + [YearModifier])
It appears that Date functions can't be used in calculated columns in tables.
You could use this to get the year, which might work in field expressions:
format(date(),"yyyy")
About your function (which I have re-written very slightly)
Year( Now() )
- Year([DOB])
- IIf( Month([DOB]) > 8
, 6
, 5 )
+ [YearModifier]
however!
I don't think you want to use now(). Which year they are in depend on their age at the 1st Sept at the start of the current academic year not now! Ok now will work until 31/dec/2015, so I must assume you will not be using the function after this date!
If you are you must use 2015 not Now().
Ok?
You can calculate the age of the children with a simple function:
Public Function AgeSimple( _
ByVal datDateOfBirth As Date) _
As Integer
' Returns the difference in full years from datDateOfBirth to current date.
'
' Calculates correctly for:
' leap years
' dates of 29. February
' date/time values with embedded time values
'
' DateAdd() is used for check for month end of February as it correctly
' returns Feb. 28. when adding a count of years to dates of Feb. 29.
' when the resulting year is a common year.
' After an idea of Markus G. Fischer.
'
' 2007-06-26. Cactus Data ApS, CPH.
Dim datToday As Date
Dim intAge As Integer
Dim intYears As Integer
datToday = Date
' Find difference in calendar years.
intYears = DateDiff("yyyy", datDateOfBirth, datToday)
If intYears > 0 Then
' Decrease by 1 if current date is earlier than birthday of current year
' using DateDiff to ignore a time portion of datDateOfBirth.
intAge = intYears - Abs(DateDiff("d", datToday, DateAdd("yyyy", intYears, datDateOfBirth)) > 0)
End If
AgeSimple = intAge
End Function
Then your expression would be something like this (ignoring the modifier):
ClassYear: IIf(AgeSimple([DOB]) > 6, 2, 1)

SSRS/TSQL date formatting

Using the following sample query below in SSRS would return month as single number(1-12). What I want to is to display the date as is something like Jan 2000. How would I go about changing my code to be able to format from single number month to a MMM YYYY format? I tried the formatting in visual studio itself but it is currently returning as MMM YYYY.
select distinct count(*) as Count,
con.STATE,
month(con.sub_Date) as Month,
year(con.sub_Date) as Year
from contract con
group by con.STATE,month(con.sub_date),year(con.sub_date)
I strongly recommend that you perform the formatting of the date at the report level.
Return the date from your query as the first date of the desired period, but then set the format string for the placeholder/text box.
The reason to do this is so that sorting and data manipulation works as expected when the report is exported to Excel.
So I would use a query as:
SELECT DISTINCT
COUNT(*) AS Count ,
con.STATE ,
DATEADD(MONTH, DATEDIFF(MONTH, 0, con.sub_Date), 0) AS FirstOfMonth
FROM
contract con
GROUP BY
con.STATE ,
DATEADD(MONTH, DATEDIFF(MONTH, 0, con.sub_Date), 0)
and then use formatting codes in the report such as MMM, YYYY for display and break the date apart with =Month(fields!FirstOfMonth.Value) if you need the components for grouping. This will allow the users to pivot the data appropriately if needed.
Formatting of the dates is presentation logic, and should be kept out of SQL if possible.
You could add the following into the query to return the pre-formatted month/year as part of the report dataset - maybe easier than trying to reconstruct it at the report level:
select distinct count(*) as Count,
con.STATE,
month(con.sub_Date) as Month,
year(con.sub_Date) as Year,
left(datename(mm, sub_Date), 3) + ' ' + cast(year(sub_Date) as char(4)) as MonthYear
from contract con
group by con.STATE,
month(con.sub_date),
year(con.sub_date),
left(datename(mm, sub_Date), 3) + ' ' + cast(year(sub_Date) as char(4))
I usually prefer to use CONVERT to get partial dates, though I don't see any CONVERT date formats that would leave you cleanly with a MON YYYY output. However, format 106 will get you most of the way there. So combining that with RIGHT() will get you the date in the format you're looking for.
SELECT DISTINCT
COUNT(*) AS Count ,
[con].[STATE] ,
MONTH([con].[sub_Date]) AS Month ,
YEAR([con].[sub_Date]) AS Year ,
RIGHT(CONVERT(CHAR(11), [con].[sub_Date], 106), 8) AS MonthYear
FROM [dbo].[contract] AS con
GROUP BY [con].[STATE] ,
MONTH([con].[sub_Date]) ,
YEAR([con].[sub_Date]) ,
RIGHT(CONVERT(CHAR(11), [con].[sub_Date], 106), 8)

Count differnce of two Date's with Respect that every month has EXACT 30 days

I thought it was clear, but doesn't seem so.
This question is about T-SQL (since it's tagged with tsql :) )
So I couldn't find any out-of-the-box solution to calculate my problem.
Let's assume you have these two dateTimes:
DECLARE #start DATETIME = '2011-01-01',
#end DATETIME = '2011-04-15'
The difference of these two datetimes in Days should be quivalent to 105.
The calculation works as follows: For every full month add 30 days, for the rest add the days till the date is achieved.
I could program this, but it would be an enormous SQL-statement, which I find find kinda ugly.
Is there any simple solution for this, like a built-in function or something short?
Thanks in advance.
Does this do the trick?
;with dates as
(
SELECT
CAST ('2011-01-01' AS DATETIME) as start_date
,CAST('2011-04-15' AS DATETIME) as end_date
)
SELECT
start_date
,end_date
,CASE WHEN DATEDIFF(MM,start_date,end_date) = 0 THEN DAY(end_date) - DAY(start_date)
WHEN DAY(start_date) = 1 THEN (30 * (DATEDIFF(MM,start_date,end_date))) + DAY(end_date)
WHEN DAY(start_date) <> 1 THEN 30 * DATEDIFF(MM,start_date,end_date) + (DAY(end_date) - DAY(start_date))
END AS gap_in_days
FROM dates
Short Answer
There's no built in function, but you could pretty easily create your own to handle converting a datetime to an int. From there, the SQL you would have to write would be trivial.
Long Answer
There's no built in function that will do this, probably because every month doesn't have 30 days. :)
You can start with this:
DECLARE #start DATETIME = '2011-01-01',
#end DATETIME = '2011-04-15'
DECLARE #endConverted INT
SELECT #endConverted = DATEPART(month, #end) * 30
+ CASE
WHEN DATEPART(DAY, #end) <= 30
THEN datepart(DAY, #end)
ELSE 30
END
DECLARE #startConverted INT
SELECT #startConverted = DATEPART(MONTH, #start) * 30
+ CASE
WHEN DATEPART(DAY, #start) <= 30
THEN DATEPART(DAY, #start)
ELSE 30
END
SELECT #endConverted - #startConverted
This isn't beautiful SQL, but it works. Note that it returns 104 (because 15 days - 1 day = 14 days), but simple enough to tack on a + 1 to the end of the final select if you want to handle the boundry days differently.
Note that the math here could pretty easily be moved into a function, which would allow you to clean your SQL up. Let's assume you created a function called GetDateTimeAsInt which holds the math; your SQL could be as simple as
DECLARE #start INT = GetDateTimeAsInt('2011-01-01'),
#end INT = GetDateTimeAsInt('2011-04-15')
SELECT #end - #start -- may need to add 1 here
In my testing, this seems to work. It will return the same result as the DATEDIFF function for the date range you specify in your post, but this is because there are 2 days with 31 days and 1 day with 28, so effectively, Jan - April have 30 days each. If you use it with a wider date range, you'll begin to get different results with my code vs. the DATEDIFF function.
Hope this helps.
I use PERIODDIFF. To get the year and the month of the date, I use the function EXTRACT:
SELECT PERIOD_DIFF(EXTRACT(YEAR_MONTH FROM NOW()), EXTRACT(YEAR_MONTH FROM time)) AS months FROM your_table;
T-sql
SELECT DATEDIFF(dd, "2011-01-01","2011-04-15")