I have an SSRS report which uses a #Year parameter, which is chosen by the user at run-time.
Fine, so far, but the SQL in the Data set Properties section contains a hard-coded date of '2010-08-31' , but, the year part of it needs to be the same as the #Year parameter which the user chooses. In other words, if you run the report in the year 2010 the results will be correct, but not if you run it now (in 2014).
The SQL at the moment is (miminum required):
SELECT DateDiff(Year, birth_dt, '2010-08-31')
--Date of Start of Academic Term
FROM table99
WHERE acad_period = #Year
...so my question is, what is the correct syntax for substituting the #Year value in place of '2010'?
EDIT : Please note that the actual format for the year is (eg) 12/13, 13/14
you can replace the line
SELECT DateDiff(Year, birth_dt, '2010-08-31')
with
SELECT DateDiff(Year, birth_dt, #Year+'-08-31')
To do the same with current date
SELECT DateDiff(Year, birth_dt, DATEPART(yyyy, getdate())+'-' + DATEPART(mm, getdate()) +'-'+DATEPART(dd, getdate()))
based on your clarification in comment, if you pass in '12/13' your query would be something like this.
SELECT DATEDIFF(Year, birth_dt, '20'+LEFT(#Year,2) + '-08-31')
FROM table99
WHERE acad_period = #Year
Since your year parameter isn't a simple year value but is instead a string like "13/14" presumably meaning the 2013/2014 school year, I would definitely handle parsing it outside of the query.
Add a computed #TermStart parameter to the dataset with the following formula:
=DateSerial(2000 + CInt(Split(Parameters!Year.Value,"/")(0)),8,31)
(So long as you aren't expecting any dates prior to 2000 of course)
Then you can use the #Year and #TermStart parameters in the query like so:
SELECT DateDiff(Year, birth_dt, #TermStart)
--Date of Start of Academic Term
FROM table99
WHERE acad_period = #Year
But as I mentioned in a comment above, that is not the correct way to calculate age. There are several ways to do that. My favorite is this:
SELECT datediff(hour,birth_dt,#TermStart)/8766
--Date of Start of Academic Term
FROM table99
WHERE acad_period = #Year
Related
this is my first submission on Stack Overflow, and I am open to any suggestions on structuring my questions.
I am new to SQL Server, and I have a line of code I that I don't understand.
Can someone please explain this?
declare #prior_year datetime = convert(date, '12/31/' + convert(varchar, datepart(yy, #start_date) - 1))
This is a problematic way to determine the last day of the year before the date(time) stored in some variable.
convert(date, '12/31/' + convert(varchar, datepart(yy, #start_date) - 1))
In English, working outward:
datepart(yy, #start_date)
Tell me the year of the variable #start_date. This is poor form because, if we mean year, we should spell out YEAR (see "Date Parts" in this post). I said this already once today, but this is just laziness, it's like "I'm going to type just enough characters to avoid an unexpected result, but not enough characters to make my intent clear." Worse is YYYY, which I see often - you have a choice between YYYY and YEAR, why not type the one that's actually a word?
datepart(yy, #start_date) - 1
--------------------------- ^^^
Subtract one from that year.I don't have any issues here, but it may be clearer to use an explicit DATEADD() against the variable first, and then extracting the year from that, since things like <some date thing> - 1 can be misread as an attempt to subtract a day (also covered in the shorthand post referenced above).
convert(varchar, datepart(yy, #start_date) - 1))
--^^^^^^^^^^^^^^^^
Convert that explicitly to a string.
Also poor form here because we should always specify the length of variable-width data. In some cases this can lead to unexpected truncation. See this post.
'12/31/' + convert(varchar, datepart(yy, #start_date) - 1))
--^^^^^^^^^^
Prefix that year with 12/31 to produce a mm/dd/yyyy string.
More poor form, because this then assumes the user has US English regional settings, MDY dateformat, etc. If you're going to insist on building a string that represents a date, always use a standard, unambiguous format: YYYYMMDD. (And FWIW YYYY-MM-DD is ambiguous, try it with SET LANGUAGE FRENCH;). See this post.
convert(date, <the rest>)
Converts the whole expression to a date.
A better solution
Ideally we should not be using strings anywhere along the line for any of this. We have built-in functions that provide all kinds of native date handling capabilities without having to worry about languages, regional settings, date format preferences, or string lengths:
SELECT DATEFROMPARTS(YEAR(#start_date) - 1, 12, 31);
-- or, more explicitly:
SELECT DATEFROMPARTS(DATEPART(YEAR, #start_date) - 1, 12, 31);
The other way you can think about this is that the last day of last year is the same day as the day before the first day of this year. Thinking about it in these terms can make it much easier to conceptualize when you are doing things like prior month, where determining the last day of the previous month is more tedious. Or if you are finding endpoints for range queries, because the end of the current reporting period is never as deterministic as the start of the next reporting period. More on that here.
SELECT DATEADD(DAY, -1, DATEFROMPARTS(YEAR(#start_date), 1, 1));
Also of potential use:
Dating Responsibly
Simplify Date Period Calculations in SQL Server
SQL Server DateTime Best Practices
Four short videos in this series
Reading this from the inside out:
We're first finding the year of the date using DATEPART and then subtracting 1 so for a value for today's date: 2020-08-18 we'd be getting an integer value of 19
datepart(yy, '2020-08-18') - 1)
We're then using convert on that value to change it to a varchar:
convert(varchar, 19)
We're then using that new varchar to create a string:
'12/31/' + '19'
Finally we're using convert again to create a date from the string
convert(date, '12/31/19')
Tony,
For completeness please provide the definition and assignment for #start_date. Assuming the statement you provided works, it is probably defined something like
declare #start_date date
set #start_date = '07-01-2020'
Working from the inside out, we can then break the statement down like so ...
This will extract the year value
datepart(yy, #start_date)
This will subtract 1 from the #start_date year value, assuming 2020, this returns 2019
convert(varchar, datepart(yy, #start_date) - 1)
And then this will convert that to the last day of the previous year.
convert(date, '12/31/' + convert(varchar, datepart(yy, #start_date) - 1))
So the statement simply sets the new field to the last day of the prior year.
declare #prior_year datetime = convert(date, '12/31/' + convert(varchar, datepart(yy, #start_date) - 1))
This SQL code declares a #prior_year datetime variable that is hard coded to 12/31/. datepart is used to extract the previous year from another datetime variable #start_date` that is passed in and tacks the returned value onto the end of the '12/31/' hard coded string. So; its really just a formula of 12/31/(#start_date prior year)
So if #start_date is 8/20/2020
You will end up with the output of 12/31/2019
declare #start_date datetime = getdate()
declare #prior_year datetime = convert(date, '12/31/' + convert(varchar, datepart(yy, #start_date) - 1))
select #prior_year
Result:
I am using DATEADD(year, -3, GETDATE()) to pull the last thee years worth of data on a rolling period.
I also have a column called Year, the view I am creating is using the MyTable.Year and not the sql keyword 'year'.
e.g DATEADD(MyTable.year, -3, GETDATE())
It resolves it every time in the view which is really annoying. I'm a bit rusty, been out of this for about 4 years.
How do I make sure it uses the keyword 'year', I find it strange it is doing this. Any explanation on this would also be helpful. SQL Server 2016
Thanks guys.
EDIT: I have edited my misplaced schema notation and identified the table name, sorry for confusion
I am not sure if I understand correctly, but you want to enforce the keyword, so you want to subtract 3 years from the current date, correct? This should work:
SELECT DATEADD(yy, -3, GETDATE())
or
SELECT DATEADD(yyyy, -3, GETDATE())
(unless you have columns named yy and yyyy ;-))
Otherwise forgive my misunderstanding...
It seems your editor has problems with the keyword YEAR and replaces it with a value from a column that is also called year
This can be solved by using a synonym for the keyword year in the DateAdd function.
So instead of
dateadd(year, -3, getdate())
use
dateadd(yy, -3, getdate())
You could put your table in a subquery and alias [Year] column.
Something like
SELECT ... FROM (SELECT [Year] AS "MyYear" FROM ...) X
(or use CTE)
Then Year would only mean the keyword.
We've been using MS Access, with the following syntax for MTD Data that works for us:
Between DateSerial(Year(Date()),Month(Date()),1)
And DateSerial(Year(Date()),Month(Date())+1,0)
We need to transition the above logic to SQL/SSRS for automatic emailed reports, but I cannot get this DateSerial logic to work with SQL.
In the Filter field of the SQL query, I can successfully use BETWEEN '8/1/2014' AND '8/31/2014' for MTD data, but would like to have a DateSerial logic applied so that reports don't need to be created for every month, quarter, year, etc.
When trying to use the DateSerial function, we get the error "Invalid or missing Expression". I've seen a few topics on this that Parameters are required, but really believe that this is a simple syntax issue for the filter field, since actual dates work with the BETWEEN command.
There are several different ways to get this. Here is just one way.
Get todays date. In this case 8/27/2014
Declare #Today date = cast(getdate() as date)
Get the first of the month, 26 days in the past
Declare #StartDate date = dateadd(d, -1 * (day(#Today) - 1), #Today)
select #Today, #StartDate
You can use the function CONVERT:
http://msdn.microsoft.com/en-us/library/ms187928.aspx
Or the function DATEFROMPARTS if you are using SQL Server 2012:
http://msdn.microsoft.com/en-us/library/hh213228.aspx
Or DATEADD:
select DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()), 0); -- first day of current month
select DATEADD(MONTH, DATEDIFF(MONTH, -1, GETDATE()), -1) -- last day of current month
This last one I took from: https://stackoverflow.com/a/11746042/1274092
See mine at:
http://sqlfiddle.com/#!3/d41d8/38333
This has been resolved. The ODBC driver does not apparently play well with SSRS. The DateSerial command would not work within the query itself. The workaround was to add the filter to the Dataset. This syntax is what works, but again only in the Dataset filter: [expression] Between [first value box] =DateSerial(Year(Now()),1,1) [second value box] =DateSerial(Year(Now()),12,31)
This gives us the YTD reporting data that we require.
I have a stored procedure that uses selects like the following which works fine so far.
In this case for example it selects all records with a date from the previous month, i.e. March 2014 (column: dateEsc, formatted as nvarchar(20), example date: 2014-03-25).
My Select (example):
SELECT COUNT(*) AS groupCount
FROM Log_Esc
WHERE
CONVERT(DATE, dateEsc, 120) >= CONVERT(DATE, CONVERT(VARCHAR(6), DATEADD(month, -1, GETDATE()), 112) + '01', 112)
How do I have to change this if instead of the current Date (GETDATE()) I want to use a variable date input as the reference.
This input would be any date and is formatted as nvarchar(20) as well, example: 2014-04-03.
So instead of calculating the previous month compared to the current month from GETDATE() I would like to calculate the same from the variable date input.
Many thanks for any help with this, Tim.
First of all I think this query is better than the one you have:
SELECT COUNT(*) AS groupCount
FROM Log_Esc
WHERE DATE >= dateadd(month,datediff(month,0,dateadd(month,GETDATE(),-1)),0)
AND DATE < dateadd(month,datediff(month,0,GETDATE()),0)
If there is an index on the DATE field this can do a seek.
If you have a parameter #indate defined as date or datetime then this will work
SELECT COUNT(*) AS groupCount
FROM Log_Esc
WHERE DATE >= dateadd(month,datediff(month,0,dateadd(month,#indate,-1)),0)
AND DATE < dateadd(month,datediff(month,0,#indate),0)
See this question for more information on flooring a date to a month: Floor a date in SQL server
So what you want is a parameter:
Specifying Parameters in a Stored Procedure
Parameters allow you to pass user input to modify output.
An example
CREATE PROCEDURE dbo.Param1
#param int
AS
BEGIN
select 7 *#param as Value
END
EXEC dbo.Param1 5 -- 7 *5
EXEC dbo.Param1 -10 -- 7 * -10
Perhaps this'll give you some creative ideas for how you might implement parameters to accomplish your group count.
SELECT Date_Received, DateAdd(Year, DateDiff(year, Cast('3/01/2010 12:00:00AM' as DateTime) ,
GetDate())-1, Cast('3/01/2010 12:00:00AM' as DateTime)) as minimum_date
FROM [Volunteers].[dbo].[Applications]
WHERE Date_received >= DateAdd(Year, DateDiff(year, Cast('3/01/2010 12:00:00AM' as DateTime),
GetDate())-1, Cast('3/01/2010 12:00:00AM' as DateTime))
In several subqueries where I need to check that a date is within an acceptable range. I need to avoid using a simple constant as I really don't want to update it or a config file each new school year.
My current solution is to enter the date into the query and use some complicated DATEADD tricks to get the current year(or previous year) into the date I am using in the comparison. The exact code is above. Is there a cleaner way for me to do this?
Thanks
Edit
The business requirement is to find applications submitted between 3/01 and 7/31.
We are running background checks and it costs us money for each check we do. Identifying applications submitted during this time period helps us determine if we should do a full, partial or no background check. I will also need to check if dates concerning the previous year.
We will be doing this every year and we need to know if they were in the current year. Maintaining the queries each year to update the dates is not something I want to do.
So I am looking for a good technique to keep the year parts of the dates relevant without having to update the query or a config file.
Old TSQL trick: cast the date to a string in a format that starts with the four-digit year, using substring to take the first four characters of that, cast it back to a date.
Actually, the reason that it's an old TSQL trick is that, if I recall correctly, there wasn't a year() function back then. Given that there's one now, using year( getdate() ) , as others' have answered, is probably the better answer.
SELECT YEAR(GETDATE())
will give you the current year.
If you need to query by month and year a lot, you should also consider making those properties into persisted, computed fields:
ALTER TABLE dbo.Applications
ADD DateReceivedMonth AS MONTH(Date_Received) PERSISTED
ALTER TABLE dbo.Applications
ADD DateReceivedYear AS YEAR(Date_Received) PERSISTED
SQL Server will now extract the MONTH and YEAR part of your Date_Received and place them into two new columns. Those are persisted, e.g. stored along side with your table data. SQL Server will make sure to keep them up to date automatically, e.g. if you change Date_Received, those two new columns will be recomputed (but not on every SELECT).
Now, your queries might be a lot easier:
SELECT (list of fields)
FROM dbo.Applications
WHERE DateReceivedYear = 2010 AND DateReceivedMonth BETWEEN 3 AND 7
Since these are persisted fields, you can even put an index on them to speed up queries against them!
Is there any reason you cannot simply use the Year function?
Select Date_Received
, Year(GetDate())
- Year('3/01/2010 12:00:00AM') - 1
+ Year('3/01/2010 12:00:00AM')
From [Volunteers].[dbo].[Applications]
Where Date_received >= ( Year(GetDate())
- Year('3/01/2010 12:00:00AM') - 1
+ Year('3/01/2080 12:00:00AM') )
Another way would be to use a common-table expression
With Years As
(
Select Year(GetDate()) As CurrentYear
, Year('3/01/2010 12:00:00AM') As ParamYear
, Year('3/01/2080 12:00:00AM') As BoundaryYear
)
Select Date_Received
, CurrentYear - Years.ParamYear - 1 + Years.ParamYear
From [Volunteers].[dbo].[Applications]
Cross Join Years
Where Date_received >= ( Years.CurrentYear
- Years.ParamYear - 1 + Years.BoundaryYear )
TSQL Function returns four digit year dependent on year. This behaves much like the standard SQL YEAR functions [Thomas - nod] which 'CAN' be tweaked using sp_configure on the advanced options, however, the code below is provided as a framework for CUSTOM requirements and can be modified as required. e.g. return as int, use with standard DATETIME functions in SQL to achieve what is needed. e.g. When working with "dirty" data I had to migrate, I used it with the PATINDEX() function to strip non-numeric values etc.
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author: Andrew McLintock
-- Create date: 13 July 2016
-- Description: Return 4-digit YEAR
-- =============================================
/*
SELECT Staging.fn_4year('06')
SELECT Staging.fn_4year('56')
SELECT Staging.fn_4year('99')
SELECT Staging.fn_4year('1906')
SELECT Staging.fn_4year('2025')
*/
CREATE FUNCTION Staging.fn_4year
(
#year_in varchar (4)
)
RETURNS varchar(4)
AS
BEGIN
DECLARE #yeartmp int, #Retval varchar(4)
SET #yeartmp = CAST(REPLACE(#year_in,' ','') AS INT)
IF LEN(CAST(#yeartmp AS Varchar)) = 4
BEGIN
Return cast(#yeartmp as varchar(4))
END
IF LEN(#year_in) = 2
BEGIN
SET #Retval = CAST(iif(#yeartmp > 49, #yeartmp + 1900, #yeartmp + 2000) AS varchar(4))
END
RETURN #Retval
END
GO
Consider keeping a set of datetime variables help readability and maintainability. I'm not sure I've captured all your requirements, especially with reference to 'previous year'. If it's as simple as finding applications submitted between 3/01 and 7/31, then this should work. If you need to determine those that were submitted Aug 1 (last year) through Feb 28 (current year), this solution could be modified to suit.
DECLARE #Start smalldatetime, #End smalldatetime, #CurrYear char(4)
SELECT #CurrYear = YEAR(getdate())
SELECT #Start = CAST( 'mar 1 ' + #CurrYear as smalldatetime),
#End = CAST( 'jul 31 ' + #CurrYear as smalldatetime)
SELECT *
FROM Applications
WHERE Date_Received
BETWEEN #Start AND #End