SAS subsetting based on a macro variable - date

I have created a macro variable which is the datetime at the time the program is run less one month, formatted to datetime20:
%let startDate = %sysfunc(intnx(dtmonth, %sysfunc(datetime()), -1), datetime20.);
This part works correctly (if I run it right now, it returns 01JUL2015:00:00:00), but what I want to do is subset a dataset based on this date in a PROC SQL statement. Basically I want to keep everything where the date occurs in the last month. My code is:
proc sql;
create table work.last_month as
select * from work.existing_table
where date >= &startDate.;
quit;
The column "date" and the variable "startDate" are both of type 'datetime20', but it is still throwing an error:
ERROR 22-322: Syntax error, expecting one of the following: ;, !, !!, &, *, **, +, -, /, <, <=,
<>, =, >, >=, AND, EQ, EQT, EXCEPT, GE, GET, GROUP, GT, GTT, HAVING, INTERSECT,
LE, LET, LT, LTT, NE, NET, NOT, OR, ORDER, OUTER, UNION, ^, ^=, |, ||, ~, ~=.
I don't know why it is throwing this error. What am I doing wrong?
Thanks!

The macro processor is a code generator so your code is generating
proc sql;
create table work.last_month as
select * from work.existing_table
where date >= 01JUL2015:00:00:00;
quit;
This isn't valid SAS code.
You can either use the SAS datetime number rather than convert/format it to a datetime value, or create a date literal for comparison.
To create a date literal, enclose the macro variable in quotes and end it with dt to indicate a datetime value.
proc sql;
create table work.last_month as
select * from work.existing_table
where date >= "01JUL2015:00:00:00"dt;
quit;
To create a SAS datetime numeric value, remove the format in the %sysfunc().
%let startDate = %sysfunc(intnx(dtmonth, %sysfunc(datetime()), -1));
proc sql;
create table work.last_month as
select * from work.existing_table
where date >= &startDate.;
quit;

Your problem is you are formatting the result - don't do that. Remember, macro variables are just creating text, they're not actual variables - they don't have concepts like "formats" (where a regular SAS variable is able to be the numeric date value AND the "pretty" formatted text).
1 %let startDate = %sysfunc(intnx(dtmonth, %sysfunc(datetime()), -1), datetime20.);
2 %put &=startdate.;
STARTDATE=01JUL2015:00:00:00
So:
proc sql;
create table work.last_month as
select * from work.existing_table
where date >= 01JUL2015:00:00:00;
quit;
That's illegal, because that's not a datetime value, it's a nice pretty human readable datetime that SQL/SAS looks at and doesn't know what to do with.
You can either drop the format bit from the %sysfunc, or add "..."dt around it to make it a datetime constant.
data existing_table;
do date=datetime()-86400*50 to datetime() by 86400;
output;
end;
run;
proc sql;
create table work.last_month as
select * from work.existing_table
where date >= "&startdate."dt;
quit;

Related

Insert date into date9

Within my PROC SQL snipped I am trying to compare a column of datatype date9. to the date 31.12.2015'. I tried:
test_date = '31DEC2015'
This returns me the following error:
ERROR: Expression using equals (=) has components that are of different data types.
What would be the correct syntax?
Add "d" to the end of the quoted date value:
test_date = '31DEC2015'd;

Correct Syntax for a TSQL query to Compare Dates

I am looking for the correct syntax to test in a TSQL WHERE clause if a
datetime2(7) type is equal to another.
WHERE (CAST(modifiedDate AS DATETIME) = '9/29/2016 3:24:24 PM')
I also tried
WHERE (CAST(modifiedDate AS DATETIME) LIKE '9/29/2016 3:24:24 PM')
And
WHERE (CAST(modifiedDate AS datetime2) = CAST('09/29/2016 3:24:24 PM' AS datetime2))
I believe I have the right hand side stated incorrectly, but that is the exact value in the database.
I am looking for all records that match that datetimestamp.
To be clear I did try to search for other results..'
I thought this was a little flaky for a search result on this site.
DB field type...
To compare DATETIME values, you need DATETIME values to compare. You can use the TSQL CONVERT function to convert a string to the DATETIME datatype. For example:
CONVERT(DATETIME, '2016-09-28 15:34:00', 20)
Note that the third argument is the "style". Example above uses style 20, the ODBC Canonical style YYYY-MM-DD HH:MI:SS (24 hour clock). There are several other styles available, maybe you find one that matches your string format. (If you can't find a match, then you will need to use some string manipulation functions to reformat your string into a format where a style is available.)
As the second argument, you can use a string literal (as shown in the example above), or you can use a reference to a CHAR or VARCHAR column.
Reference: CAST and CONVERT (Transact-SQL)
On a different, but related, note: Why are date time values being stored as strings in the database, and not DATETIME datatype?
If the datatype of the column is DATETIME2(7), then I think you would want to compare DATETIME2(7) datatypes.
If we do an "equal to" comparison, that's going to be an exact match, including the fractional seconds. If you want to match DATETIME2(7) values from a given second, you could use a range comparison:
WHERE t.my_col_datetime2_7 >= '2016-09-29 15:24:24'
AND t.my_col_datetime2_7 < '2016-09-29 15:24:25'
Note the format of the string literals allowed for comparison to DATETIME2 are YYYY-MM-DD HH:MI:SS (24 hour clock) with optional fractional seconds .nnnnnnn.
Reference: Supported String Literal Formats for datetime2
Not sure where you are having issues. I guess it depends how you are storing your date value. If it's datetime2, this works fine.
DECLARE #testtable TABLE(
ID INT
, modifieddate DATETIME2)
INSERT INTO #testtable
(id, modifieddate)
VALUES
(1, '9/29/2016 3:24:24 PM'),
(2, '2/01/2016 3:24:24 PM'),
(3, '6/25/2016 3:24:24 PM')
SELECT *
FROM #testtable
SELECT *
FROM #testtable
WHERE CAST(modifieddate AS DATETIME2) = '09/29/2016 3:24:24 PM'
SELECT *
FROM #testtable
WHERE modifieddate = '9/29/2016 3:24:24 PM'
SELECT *
FROM #testtable
WHERE modifieddate = CAST('09/29/2016 3:24:24 PM' AS DATETIME2)

use a character date string as a date in data step

I have the following two macro variables:
%let start_date = 29MAY2014;
%let end_date = 15JUL2014;
I would like to create a dataset which is a series of dates between these (inclusive.) I cannot change the input format of the macro variables &start_date and &end_date.
I have tried many variations of the following, but SAS spits out an error for each:
data base_dates;
do date = put("&start_date",date9.) to put("&end_date",date9.);
output;
end;
format date date11.;
run;
Any help in this would be much appreciated
Use them as date literals, enclose in quotes and add a d at the end.
Do date = "&start_date"d to "&end_date"d;
It was simple; input() instead of put()
data base_dates;
do date = input("&start_date",date9.) to input("&end_date",date9.);
output;
end;
format date date11.;
run;

Creating a date exactly one month in the past in SAS

I am trying to create two datetime variables in SAS 9.3. The first, "endDate" is the current datetime at the time the program is run. The second, "startDate" is exactly one month in the past.
My code is:
%let endDate = %sysfunc(DATETIME() datetime.);
%let startDate = %sysfunc(intnx('month', DATETIME(), -1) datetime.);
Based on any documentation I can find, I can't figure out what is wrong with it, but I am getting the following error message:
"ERROR: Expected close parenthesis after macro function invocation not found."
What am I doing wrong?
Some additional background: I want to use these two macro variables inside a proc sql statement so I can filter a table to data from within the past month at the run-time.
Thanks!
You have a couple of issues:
%sysfunc() takes two parameters, the second one is optional, but it does need to be separated by a comma.
Your use of DateTime() function in the intnx function also requires a %sysfunc()
You have datetime variables so you need to use DTMONTH as your interval instead of month.
You don't need quotes around literals in a macro call
%let endDate = %sysfunc(DATETIME(), datetime.);
%put &endDate;
%let startDate = %sysfunc(intnx(dtmonth, %sysfunc(datetime()), -1), datetime.);
%put &StartDate;
Four things:
You don't need the single quotes around string arguments when calling functions via %sysfunc
You need to use a %sysfunc for every individual data step function you want to call in the macro environment, which means you need to nest them in this case as you're calling one function inside another. This can get messy.
You need to increment your datetime by a datetime-month increment, not a month increment, so use dtmonth instead of month.
You need a comma between the function call and the format within %sysfunc
Putting it all together:
%let endDate = %sysfunc(DATETIME(), datetime.);
%let startDate = %sysfunc(intnx(dtmonth, %sysfunc(DATETIME()), -1), datetime.);
%let endDate = %sysfunc(DATETIME(), datetime21.);
%let startDate =%sysfunc(putn(%sysfunc(intnx(dtmonth, %sysfunc(DATETIME()), -1,same)),datetime21.));
%put &enddate &startdate;

SAS how to change a date format in a where clause

I have some data which has the date stored in the yymmn6. format and others that are stored in the date9. format
I want to do a proc sql statement using the where clause but in order to compare two dates the date9. fields need to be in the yymmd6. format
is there an equivalent of this which can be used in the where clause
proc sql;
select dt format=yymmn6.,
from have;
quit;
i.e.
proc sql;
select *
from have WHERE dt format=yymmn6. = other_date;
quit;
A SAS variable formatted as a date is simply a number (representing days since 01JAN1960).
If other_date is also a SAS date value (regardless of format), you don't need to apply a format to either variable.
If other_date is a string representing a date (e.g. 201501), you can apply the format to dt, or input other_date to a SAS date.
/* Apply format to dt to match other_date */
proc sql ;
select *
from have
where put(dt,yymmn6.) = other_date ;
quit ;
/* Be careful here though as 201501 will input as 2015-01-01 */
proc sql ;
select *
from have
where dt = input(other_date,yymm6.) ;
quit ;