I'm having problems to DATEs in SAS Enterprise Guide 7.1 M4.
it's very very simple in SQL Server or VBA but in SAS is driving me crazy.
Problem:
For some strange reason I'm unable to make a simple select. I tried many different forms of formating and convertions but any seems to work
My Simple select returns no observations.
Description of T1.DT_DATE in proc contents
Type: Num
Len: 8
Format: DDMMYY10.
Informat: DATETIME20.
%let DATE_EXAMPLE='01JAN2019'd;
data _null_;
call symput ('CONVERTED_DATE',put(&DATE_EXAMPLE, ddmmyy10.));
run;
%put &CONVERTED_DATE;
PROC SQL;
CREATE TABLE TEST_SELECT AS
SELECT *
FROM MY_SAMPLE_DATA as T1
WHERE T1.DT_DATE = &CONVERTED_DATE
;QUIT;
Intially you are setting up the date properly but you are changing it to a different value that is not understood in where clause. See the resolutions of macrovariable for both macrovariables you have created
%put value of my earlier date value is &DATE_EXAMPLE;
value of my earlier date value is '01JAN2019'd
%put value of my current date value is &CONVERTED_DATE;
value of my current date value is 01/01/2019
change your code to use date literal that is '01JAN2019'd then your code will work. 01/01/2019 value will not make sense in where clause.
PROC SQL;
CREATE TABLE TEST_SELECT AS
SELECT *
FROM MY_SAMPLE_DATA as T1
WHERE T1.DT_DATE = &CONVERTED_DATE
;QUIT;
Related
I've several SAS (PROC SQL) queries using a MIN(startdate) and MAX(enddate).
To avoid having to calculate these every time I want to do this once at the beginning and store it in a macro variable but I get an error every time.
What is going wrong or how to achieve this ?
Thanks in advance for the help !
This works:
WHERE DATE BETWEEN
(SELECT MIN(startdate format yymmddn8. FROM work.mydata)
AND (SELECT MAX(enddate format yymmddn8. FROM work.mydata)
DATE format is YYMMDD8n and length is 8.
Creating macro variables:
PROC SQL;
SELECT MIN(startdate), MAX(enddate)
INTO :start_date, :end_date
FROM work.mydata
QUIT;
/*Formatting the macro variable:*/
%macro format(value,format);
%if %datatyp(&value)=CHAR
%THEN %SYSFUNC(PUTC(&value, &format));
%ELSE %LEFT(%QSYSFUNC(PUTN($value,&format)));
%MEND format;
Tried:
WHERE DATE BETWEEN "%format(&start_date, yymmddn8.)" AND "%format(&end_date, yymmddn8.)"
Error message:
ERROR: Expression using equals (=) has components that are of different data types
First, you are missing d when providing date for BETWEEN operator.
WHERE DATE BETWEEN "%format(&start_date, yymmddn8.)"d AND "%format(&end_date, yymmddn8.)"d
But keep in mind tht date string must be in date9. format.
"4NOV2022"d
Second, you dont need to format date for this WHERE condition. Date is numeric and numeric value whould work fine.
WHERE DATE BETWEEN &start_date AND &end_date
If you really want to have date formated you can format it directly inside PROC SQL:
PROC SQL;
SELECT
MIN(startdate) format=date9.,
MAX(enddate) format=date9.
INTO
:start_date,
:end_date
FROM
work.mydata
QUIT;
and then
WHERE DATE BETWEEN "&start_date"d AND "&end_date"d
Note that in a PROC SQL query the format attached to a variable does not carry over to the result of aggregate functions, like MIN() and MAX(), performed on the variable. For numeric variables PROC SQL will use the BEST8. format when converting the number into a string to store into the macro variable. You can remove the extra spaces that causes by adding the TRIMMED keyword.
proc sql noprint;
select min(startdate), max(enddate)
into :start_date trimmed
, :end_date trimmed
from work.mydata
;
quit;
Do not add quotes around the values generated by expanding the macro variables. That would generate a string literal and not a numeric literal.
where date between &start_date and &end_date
If you want the values put into the macro variables by the into syntax to be formatted in some other way you need to attach the format as part of the query.
For example if you wanted the value to be something that could be used to generate a date literal, that is a string that the DATE informat understands, then use the DATE format. Make sure the width used is long enough to include all four digits of the year.
proc sql noprint;
select min(startdate) format=date9.
, max(enddate) format=date9.
into :start_date trimmed
, :end_date trimmed
from work.mydata
;
quit;
...
where date between "&start_date"d and "&end_date"d
I want to convert the Db2 timestamp into SAS numeric
proc sql;
connect to db2 ;
create table db2ts as
select * from connection to db2
(select char(current timestamp)
from sysibm.sysdummy1)
as db2ts(timestm);
%put &sqlxmsg ;
%put &sqlxrc ;
disconnect from db2;
quit;
data _NULL_;
set DB2TS;
putlog 'timestmb--' timestm;
datets =input(timestm,yymmdd10.);
timets =input(substr(timestm,12),time15.);
dt2=dhms(datets,0,0,timets);
format datets date9. timets time15.6 dt2 datetime26.6;
putlog 'currdatets:' dt2;
call symput('currdatets',catx('-',put(datepart(dt2),yymmdd10.),
translate(put(dt2,tod15.6),'.',':')));
putlog 'currdatets:' currdatets;
run;
timestmb--2022-05-18-16.44.54.587001
currdatets:18MAY2022:16:44:54.587001
%put currdatets: &currdatets;
currdatets: 2022-05-18-16.44.54.587001
Proc sql:
Insert into table1
(Time, Type)
Values
(%sysfunc(quote(&currdatets)), 'A')
Error:
ERROR: Value 1 of VALUES clause 1 does not match the data type of
the corresponding column in the object-item list(in the SELECT clause).
how can I use the macro variable currdatets to insert into the DB2 table?
So your first query is not valid SQL syntax so I am not sure how it even works. Try something like:
create table db2ts as
select * from connection to db2
(select char(current timestamp) as timestmp
from sysibm.sysdummy1)
;
This will create a dataset name db2ts with a character variable named timestmp;
Which should be the similar to what you would get with this data step.
data db2ts ;
timestmp ='2022-05-17-12.02.43.387486';
run;
Now you can convert the string into a DATETIME value by first converting it into a DATE and a TIME value and then combining them.
data want ;
set db2ts;
date=input(timestmp,yymmdd10.);
time=input(substr(timestmp,12),time15.);
datetime=dhms(date,0,0,time);
format date date9. time time15.6 datetime datetime26.6;
put (_all_) (=/);
run;
Results:
timestmp=2022-05-17-12.02.43.387486
date=17MAY2022
time=12:02:43.387486
datetime=17MAY2022:12:02:43.387486
It would be easier if you could figured out if DB2 has a function, or option for the CAST() function, that can let you specify how the string is generated so that it is something that SAS can recognize immediately as a datetime.
I created below SAS code to pull the data for particular a date.
%let date =2016-12-31;
proc sql;
connect to teradata as tera ( user=testuser password=testpass );
create table new as select * from connection tera (select acct,org
from dw.act
where date= &date.);
disconnect from tera;
quit;
There are situation where that particular date may be missing in the dataset due to holiday.
I thinking how to query the previous date(non-holiday) if the mention date in the %let statement is holiday
Before running your query you have to do a lookup or data check on the date you are using. You have two options:
Use a Date Dimension table in order identify/lookup holidays.
Count how many records you have for that date, if you get 0 obs for this date, use date+1 in your query.
I recommend using the date dimension table option.
Teradata has Sys_Calendar.Calendar view. You can use that in query, it has all the information regarding weekdays and others.
if you want to SAS way use weekday function and use call symput as shown below. Teradata needs single quote around the date, so it is better to have single quotes around when creating macro variable
data _null_;
/* this is for intial date*/
date_int = input('2016-12-31', yymmdd10.);
/* create a new date variable depending on weekday*/
if weekday(date_int) = 7 then date =date_int-2; /*sunday -2 days to get
friday*/
else if weekday(date_int) = 6 then date =date_int-1;/*saturday -1 day to get
friday*/
else date =date_int;
format date date_int yymmdd10.;
call symputx('date', ''''||put(date,yymmdd10.)||'''');
run;
%put modfied date is &date;
modified date is '2016-12-29'
Now you can use this macro variable in your pass through.
I have created a stored process in SAS that prompts the user to select a month/year combination which looks like this 2015_10. Then from the next box they can click on a calendar and select a startdate which is a timestamp and an enddate also a timestamp.
I would like to combine this into one step, where the user only selects the start and end date. However, my source table is in SQL Server, and the tables are partitioned by months, and the tables are named like this datatabel_2015_10 where the last two digits represent the month. Once the user selects the month, I have proc sql query that table, and then after that there is another query to pull only the rows which fall between the start date and end date, those are time1 and time2 stored as character strings in MS SQL Server which look like this 30JAN2015:19:52:29
How can I code this up so as to eliminate the month/year prompt and only have two selections, namely startdatetime and enddatetime, and still get the query.
Concatenating the monthly tables is not an option because they are huge and runs forever even if I use a pass through query.
Please help.
Thanks
LIBNAME SWAPPLI ODBC ACCESS=READONLY read_lock_type=nolock noprompt="driver=SQL server; server=XXX; Trusted Connection=yes; database=XXX" schema='dbo';
proc sql;
create table a as
select
startdate_time,
enddate_time
from SWAPPLI.SQL_DB_2015_10
quit;
proc sort data=a out=b;
by startdate_time, enddate_time
where enddate_time between "&startdate"dt and "&enddate"dt;
run;
You can delete a step by asking directly the timestamp begin and end.
Then you can deduce the YEAR and MONTH from the timestamp selected.
For &startdate = 30JAN2015:19:52:29, you can create 2 macro-variable by creating 2 substring following this :
%let startdate = 30JAN2015:19:52:29;
%let YEAR=%SUBSTR("&startdate",4,3);
%let MONTH=%SUBSTR("&startdate",7,4);
%PUT YEAR=&YEAR;
%PUT MONTH=&MONTH;
Result :
%PUT YEAR=&YEAR;
YEAR=JAN
%PUT MONTH=&MONTH;
MONTH=2015
Then you can create a %if condition to match JAN with 01, FEB with 02, ect...
Here MONTH will be 01
So you don't need to ask the information 2 times any more.
Then you can select your dataset by doing this :
proc sql;
create table a as
select
startdate_time,
enddate_time
from SWAPPLI.SQL_DB_&YEAR._&MONTH.
quit;
proc sort data=a out=b;
by startdate_time, enddate_time
where enddate_time between "&startdate"dt and "&enddate"dt;
run;
You should probably restrict the selection to a MONTH and not allow the selection through several month like start=01JAN and end=01MAR. Because the selection will be only on the dataset SQL_DB_2017_01 and not taking into account the end=01MAR.
The following works fine for me, as I want to select the YYYYQ value for a column to show the year and quarter:
proc sql;
select YYQ(year(datepart(betdate))
, QTR(datepart(betdate))) FORMAT=YYQN. as yearquarter
, QTR(datepart(betdate)) as semiyear
from &dsn;
quit;
How can I calculate the 'SEMIYEAR' instead of QTR? I can find refernces to it in the SAS documentation, but can't seem to get it to work. I want to show YYYYS, as it the year and the year 'half'.
Thanks.
There's not exactly a format or function for that, unfortunately. Do you need the date part of the value to persist, or just the value "20131"? (In your YYQ, for example, the underlying value is an actual date, which corresponds to the first date in the period of 2013Q1, so Jan 1; it's just displayed as 20131).
If you just want to display the value, you can do something pretty simple, like this:
proc sql;
select YYQ(year(datepart(betdate))
, QTR(datepart(betdate))) FORMAT=YYQN. as yearquarter
, floor(QTR(datepart(betdate))/2)+1 as semiyear
from test;
quit;
And append on the year if you want. However that does not maintain the actual first-day-of-the-period value. If you want to do that, you should use INTNX:
proc sql;
select YYQ(year(datepart(betdate))
, QTR(datepart(betdate))) FORMAT=YYQN. as yearquarter
, intnx('SEMIYEAR',datepart(betdate),0,'b') FORMAT=DATE9. as semiyear
from test;
quit;
That doesn't format it neatly, of course, so you would have to write your own format, unless I'm missing one that exists already; that's pretty easy though.
proc format;
value SEMIYEAR
'01JAN2013'd-'30JUN2013'd = '20131'
'01JUL2013'd-'31DEC2013'd = '20132'
;
quit;
Sadly you can't use picture formats to do this as far as I know - the documentation at least doesn't offer an option to display semiannual period. You can either do like I did above and just explicitly specify the time periods in the range, or you can write a function format; see http://support.sas.com/documentation/cdl/en/proc/65145/HTML/default/viewer.htm#n1eyyzaux0ze5ln1k03gl338avbj.htm for examples of how to do that.
Edit: Here's an example that mostly works.
proc fcmp outlib=work.functions.smd;
function sfmt(date) $;
length snum $5;
snum = put(year(date)*10+floor(QTR(date)/2)+1,5.);
return(snum);
endsub;
run;
options cmplib=(work.functions);
proc format;
value semiyear
other=[sfmt()];
quit;
data test2;
set test;
x=put(datepart(betdate),semiyear.);
put x=;
run;
proc sql;
select YYQ(year(datepart(betdate))
, QTR(datepart(betdate))) FORMAT=YYQN. as yearquarter
, intnx('SEMIYEAR',datepart(betdate),0,'b') FORMAT=SEMIYEAR5. as semiyear
from test;
quit;
However, for some odd reason in my session at least the PROC SQL returns goofy characters instead of 20131. The data step returns the correct answer in the log. Not sure if this is a bug or if i'm doing something very slightly wrong.