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
Related
I am handing over some code to a colleague, which is to be run daily to generate reports.
Once every month a new cycle starts, and we have to update the code for cycle_start_date
data mtd_table;
set ytd_table;
where entry_date> '10Mar2021'd; /*different every month*/
run;
Since he'll be running them from now on, along with other reports from other teams, I don't want to bother him every month to tweak the code. So I devised this:
i run(once a month)
data shared1.cycle_start_date;
cycle_start_date='10Mar2021'd;
run;
he runs(everyday)
data mtd_table;
set ytd_table;
where entry_date>/*(select cycle_start_date from shared1.cycle_start_date)*/;
run;
I'm not sure how to correctly implement this (select cycle_start_date from shared1.cycle_start_date) part, since it is from proc sql. Would appreciate help.
When you store program parameters in a data set (called control data) one use case is having later code extract the values into macro variables, at which point other code can resolve the macro variable for replacement at (automatic) step compile and run time. Two ways to extract values into macro variables are:
Proc SQL, SELECT ... INTO :<macro-variable>, and
DATA _NULL_, CALL SYMPUT(<macro-variable>, <data step expression>);
Don't forget, macro resolution replaces the macro variable as source code text. Dates in macro variables can be either the SAS data value (the text representation of a SAS date integer) or part of a date literal (the text <dd-mon-yyyy>) that would be resolved as source date literal "&<macro-variable>"D when to be utilized as a date value. The date literal part is used when you want to show the date value as human readable in when output; for example: TITLE "cycle start: &cycle_start_date";
Control data (you)
Rebuild or edit values in data set (name it parameters to be more useful)
data shared1.parameters;
cycle_start_date = '10Mar2021'd; * stored as a SAS date value (integer);
run;
Note: Some control data layouts use a name/value organization and has one row per parameter.
Other
Extract date value as SAS date value text, and as date literal text portion and use.
proc sql noprint;
select
cycle_start_date
, cycle_start_date format=date11.
into
:cycle_start_date_value trimmed
, :cycle_start_date_literal trimmed
from
shared1.parameters
;
%put &=cycle_start_date_value;
%put &=cycle_start_date_literal;
/*
* will log the macro variable value as follows:
* CYCLE_START_DATE_VALUE=22349 and
* CYCLE_START_DATE_LITERAL=10-MAR2021
*/
data ...
set ...;
where date >= &cycle_start_date; *resolve parameter as text representation of a SAS date value (integer);
...
title "Cycle starts: &cycle_start_date_literal";
proc print data=...; * title in output shows human readable part of date;
run;
Another approach is to use a common source code file that is %included by others. You would edit or recreate the parameters file by whatever process you want.
parameters.sas
%let cycle_start_date = 10-Mar-2021;
use
%include 'parameters.sas';
data ...
set ...;
where date >= "&cycle_start_date"D; *resolve parameter as part of date literal;
...
title "Cycle starts: &cycle_start_date";
proc print data=...; * title in output shows human readable part of date literal;
run;
One possible solution would be to put the date from the cycle_start_date table that is in the shared library shared1 into a macro-variable date that will be used in your data step to filter the ytd_table table based on the entry_date variable.
proc sql noprint;
select cycle_start_date into :date
from shared1.cycle_start_date;
quit;
data mtd_table;
set ytd_table;
where entry_date > &date.;
run;
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;
I want to insert values into a new table, but I keep getting the same error: VALUES clause 1 attempts to insert more columns than specified after the INSERT table name. This is if I don't put apostrophes around my date. If I do put apostrophes then I get told that the data types do not correspond for the second value.
proc sql;
create table date_table
(cvmo char(6), next_beg_dt DATE);
quit;
proc sql;
insert into date_table
values ('201501', 2015-02-01)
values ('201502', 2015-03-01)
values ('201503', 2015-04-01)
values ('201504', 2015-05-01);
quit;
The second value has to remain as a date because it used with > and < symbols later on. I think the problem may be that 2015-02-01 just isn't a valid date format since I couldn't find it on the SAS website, but I would rather not change my whole table.
Date literals (constants) are quoted strings with the letter d immediately after the close quote. The string needs to be in a format that is valid for the DATE informat.
'01FEB2015'd
"01-feb-2015"d
'1feb15'd
If you really want to insert a series of dates then just use a data step with a DO loop. Also make sure to attach one of the many date formats to your date values so that they will print as human understandable text.
data data_table ;
length cvmo $6 next_beg_dt 8;
format next_beg_dt yymmdd10.;
do _n_=1 to 4;
cvmo=put(intnx('month','01JAN2015'd,_n_-1,'b'),yymmn6.);
next_beg_dt=intnx('month','01JAN2015'd,_n_,'b');
output;
end;
run;
#tom suggest you in comments how to use date and gives very good answer how to it efficently, which is less error prone than typing values. I am just putting the same into the insert statement.
proc sql;
create table date_table
(cvmo char(6), next_beg_dt DATE);
quit;
proc sql;
insert into date_table
values ('201501', "01FEB2015"D)
;
I have a global macro variable from another macro which looks like
'01jan2014' when you print it in the log
i.e. there are enforced quotemarks
I want to use this in a proc sql statement but I can't as it doesn't like the variable type.
How do I convert this into a date in the WHERE clause of my proc sql statement?
%let yourdate = 01Feb2015;
%put &yourdate; /* resolve to 01Feb2015 */
proc sql;
select *
from have
where date ge "&yourdate."d;
or
%let yourdate2 = '01Feb2015'd;
proc sql;
select *
from have
where date ge &yourdate2;
I think the first one is better since it won't contain ' in macro variable.
To convert a date string in DDMONYYYY format, simple add a d to the end.
'01jan2014'd
will convert the string to a date.
In your case:
&var.d
will resolve to
'01jan2014'd
which will be interpreted as a date.
I have a problem that seems pretty simple (probably is...) but I can't get it to work.
The variable 'name' in the dataset 'list' has a length of 20. I wish to conditionally select values into a macro variable, but often the desired value is less than the assigned length. This leaves trailing blanks at the end, which I cannot have as they disrupt future calls of the macro variable.
I've tried trim, compress, btrim, left(trim, and other solutions but nothing seems to give me what I want (which is 'Joe' with no blanks). This seems like it should be easier than it is..... Help.
data list;
length id 8 name $20;
input id name $;
cards;
1 reallylongname
2 Joe
;
run;
proc sql;
select trim(name) into :nameselected
from list
where id=2;
run;
%put ....&nameselected....;
Actually, there is an option, TRIMMED, to do what you want.
proc sql noprint;
select name into :nameselected TRIMMED
from list
where id=2;
quit;
Also, end PROC SQL with QUIT;, not RUN;.
It works if you specify a separator:
proc sql;
select trim(name) into :nameselected separated by ''
from list
where id=2;
run;