SAS YYMMDD10. works but YYMMDDn10 doesn't - sql

This is my data (sorry for no script, it's just proc create table from mssql):
testdb.testtable
id - numeric
date_from - numeric (datetime from mssql)
date_to - numeric (datetime from mssql)
base_id - numeric
base_id2 - string (length 64)
What I tried to do was:
proc sql;
update testdb.testtable tt
set base_id2 = CATX('_',
('data from other table'),
put(datepart(date_from),yymmddn10.),
put(datepart(date_to),yymmddn10.),
put(base_id,z4.)
)
where (....)
;
quit;
And I get this error:
The width value for YYMMDDN is out of bounds. It should be between 2 and 8.
The width value for YYMMDDN is out of bounds. It should be between 2 and 8.
What I really don't understand is that when I use format with separators, YYMMDD10., it works.
When i run:
proc sql;
select datepart(date_from) format=yymmddn10. from testdb.testtable;
quit;
It returns 20191227 - It's great. When I run
proc sql;
select put(datepart(date_from),yymmddn10.) from testdb.testtable;
quit;
It fails with the same error.
What do I miss?

There seems to be a bug in PROC SQL that allows you to attach a format that cannot work (the maximum width needed for a date without separators is 8 bytes).
It is also interesting that PROC PRINT (and the simple SELECT query in PROC SQL, like in your example) do not mind that the format width is invalid.
542 data test1;
543 now=date();
544 run;
NOTE: The data set WORK.TEST1 has 1 observations and 1 variables.
545
546 data test2 ;
547 set test1;
548 format now yymmddn10.;
----------
29
ERROR 29-185: Width specified for format YYMMDDN is invalid.
549 run;
NOTE: The SAS System stopped processing this step because of errors.
WARNING: The data set WORK.TEST2 may be incomplete. When this step was stopped there were 0 observations
and 1 variables.
WARNING: Data set WORK.TEST2 was not replaced because this step was stopped.
550
551 proc sql;
552 create table test2 as select now format=yymmddn10. from test1;
NOTE: Table WORK.TEST2 created, with 1 rows and 1 columns.
553 select * from test2;
554 quit;
555
556 proc print data=test2;
557 run;
NOTE: There were 1 observations read from the data set WORK.TEST2.
558
559 data test3;
560 set test2;
561 run;
ERROR: Width specified for format YYMMDDN is invalid.
NOTE: The SAS System stopped processing this step because of errors.
WARNING: The data set WORK.TEST3 may be incomplete. When this step was stopped there were 0 observations
and 1 variables.
WARNING: Data set WORK.TEST3 was not replaced because this step was stopped.
Also interesting is that if you use that format specification in PROC FREQ
proc freq data=test2; tables now; run;
it adds a space and a 'F7'x character in front of the data string.
The FREQ Procedure
Cumulative Cumulative
now Frequency Percent Frequency Percent
---------------------------------------------------------------
÷20200218 1 100.00 1 100.00

The number in format is the given width. Format YYMMDDn has 8 chars, so I should have used YYMMDDn8. And it worked.
I was having a long struggle with it and I still don't understand why did it work in format= and not in put().

Related

sql datetime in sas date format

proc sql noprint;
%let today=%sysfunc(today(),yymmdd10.);
%put &today.;
select
COALESCE(sum(abc.step_sum),0)
into :SumLoans_12m
from
RTDM_ABT.ABT_CONTRACT abc
where abc.CLIENT_ID = "&T_CLIENT_ID"
and abc.CONTRACT_BEGINDATE - &today. <= 360
and abc.DML_FLAG NE 1;
quit;
I understand it is necessary to convert CONTRACT_BEGINDATE (2021-01-14 00:00: 00.000) to format SAS, I think it would help , how do you do that? Thanks for your help!
ERROR:
SYMBOLGEN: Macro variable TODAY resolves to 2021-05-18
2021-05-18
SYMBOLGEN: Macro variable T_CLIENT_ID resolves to 00000031-B081-4E97-9437-F20CF874F857
MPRINT(MEQUIBEHSCOREUPRE): select COALESCE(sum(abc.step_sum),0) into :SumLoans_12m from RTDM_ABT.ABT_CONTRACT abc where abc.CLIENT_ID = "00000031-B081-4E97-9437-F20CF874F857" and
abc.CONTRACT_BEGINDATE - today() <= 360 and abc.DML_FLAG NE 1;
175369 1621341661 no_name 0 SQL (503
SQLSRV_39799: Prepared: on connection 0 175370 1621341661 no_name 0 SQL (503
SELECT * FROM rtdm_abt . ABT_CONTRACT 175371 1621341661 no_name 0 SQL (503
175372 1621341661 no_name 0 SQL (503
SAS_SQL: Unable to convert the query to a DBMS specific SQL statement due to an error. 175373 1621341661 no_name 0 SQL (503
ACCESS ENGINE: SQL statement was not passed to the DBMS, SAS will do the processing. 175374 1621341661 no_name 0 SQL (503
Try comparing the datetime variable to a datetime value. First let's rewrite your expression to make it easier to code. The comparison abc.CONTRACT_BEGINDATE - <TODAY> <= 360 is the same as abc.CONTRACT_BEGINDATE <= 360 + <TODAY>. So let's make a macro variable that contains that datetime value.
%let now_plus_360_days = %sysfunc(intnx(dtday,%sysfunc(datetime()),360)) ;
And then use that macro variable in the expression
and abc.CONTRACT_BEGINDATE <= &now_plus_360_days
proc sql noprint;
select sum(step_sum)
into :SumLoans_12m
from
RTDM_ABT.ABT_CONTRACT abc
where abc.client_id = "&T_CLIENT_ID"
and abc.DML_FLAG <> 1
and datepart(abc.CONTRACT_BEGINDATE) between today() - 365 and today()
;
quit;
It worked for me, in double it's nice that I came up with it myself))
SAS date values are integers and are the number of days from epoch 01-JAN-1960.
TODAY() returns a SAS data value, an integer nnnnn.
You are formatting that value as yymdd10. which produces a string with construct yyyy-mm-dd. (The string is more understandable to humans than the data value nnnnn.) and assigning that string to macro symbol today. When the symbol is resolved the string is inserted into your step as source code.
So,
abc.CONTRACT_BEGINDATE - &today. <= 360
becomes
abc.CONTRACT_BEGINDATE - 2021-05-18 <= 360
You want to resolve the macro symbol in the context of a SAS date literal which is construct "dd-mon-yyyy"D
Try
%let today_literal = "%sysfunc(today(),date11.)"D;
%put &=today_literal;
and
abc.CONTRACT_BEGINDATE - &today_literal <= 360
However, a lot depends on the options of the libname statement for RDTM_ABT that you do not show. You might be required to use pass-through SQL statement in order to get the result you want
connect using RDTM_ABT;
select * from connection to RDTM_ABT
(
... my query in remote database syntax ...
)
You could use the remote system functions for today, or construct the query using the appropriate source code for the remote system date literal or string to date conversion functions.

How to select a range of columns in a case statement in proc SQL?

I have around 80 columns names diag1 to diag80. I am wondering how can I pick just 30 columns and apply a case statment in proc SqL. The following code produces an error because it doesn't understand the range.
proc sql;
create table data_WANT as
select *,
case
when **diag1:diag30** in ('F00','G30','F01','F02','F03','F051') then 1
else 0
end as p_nervoussystem
from data_HAVE;
quit;
Thank you, any help is appreciated!
You have two problem with that attempted syntax. First is that variable lists are not supported by PROC SQL (since they are not supported by SQL syntax). The second is there is no simple syntax to search N variables for a list of M strings.
You will need a loop of some kind. It will be much easier in SAS code than in SQL.
For example you could make an array to reference your 30 variables than loop over the variables checking whether each one has a value in the list of values. You can stop checking once one is found.
data want;
set have;
array vars diag1-diag30;
p_nervoussystem=0;
do index=1 to dim(vars) while (not p_nervoussystem);
p_nervoussystem = vars[index] in ('F00','G30','F01','F02','F03','F051');
end;
run;
The inverse pattern to #Tom search for a nervous system diagnostic code:
via FINDW over a concatenation of the observed diagnoses
via WHICHC over an array of the observed diagnoses
data have;
infile datalines missover;
length id 8;
array dx(30) $5;
input id (dx1-dx50) (50*:$5.);
datalines;
1 A00 B00 A12
2 F00 Z12 T45
3 A01 A02 B12 F00
4 Q12
5 Q13
6 T14
7 F44 F45 F46
8 . . . . . . . . . . . . . . G30
;
data want;
length p_nervoussystem p_ns 4;
set have;
array dx dx:;
array ns(6) $5 _temporary_ ('F00','G30','F01','F02','F03','F051');
dx_catx = catx(' ', of dx(*));* drop dx_catx; * way 1;
do _n_ = 1 to dim(ns) until(p_nervoussystem);
p_nervoussystem = 0 < indexw(dx_catx, trim(ns(_n_))); * way 1;
p_ns = 0 < whichc(ns(_n_), of dx(*)); * way 2;
end;
run;```
try it sys.tables and sys.columns and filter your columns.
SELECT * FROM sys.tables INNER JOIN sys.columns ON columns.object_id = tables.object_id

Use SAS macro variable to create variable name in PROC SQL

I'm trying to create a set of flags based off of a column of character strings in a data set. The string has thousands of unique values, but I want to create flags for only a small subset (say 10). I'd like to use a SAS macro variable to do this. I've tried many different approaches, none of which have worked. Here is the code that seems simplest and most logical to me, although it's still not working:
%let Px1='12345';
PROC SQL;
CREATE TABLE CLAIM1 AS
SELECT
b.MEMBERID
, b.ENROL_MN
, CASE WHEN (a.PROCEDURE = &Px1.) THEN 1 ELSE 0 END AS CPT_+&Px1.
, a.DX1
, a.DX2
, a.DX3
, a.DX4
FROM ENROLLMENT as b
left join CLAIMS as a
on a.MEMBERID = b.MEMBERID;
QUIT;
Obviously there is only one flag in this code, but once I figure it out the idea is that I would add additional macro variables and flags. Here is the error message I get:
8048 , CASE WHEN (PROCEDURE= &Px1.) THEN 1 ELSE 0 END AS CPT_+&Px1.
-
78
ERROR 78-322: Expecting a ','.
It seems that the cause of the problem is related to combining the string CPT_ with the macro variable. As I mentioned, I've tried several approaches to addressing this, but none have worked.
Thanks in advance for your help.
Something like this normally requires dynamic sql (although I am not sure how will that works with SAS, I believe it may depend on how you have established connection with the database).
Proc sql;
DECLARE #px1 varchar(20) = '12345'
,#sql varhcar(max) =
'SELECT b.MEMBERID
, b.ENROL_MN
, CASE WHEN (a.PROCEDURE = ' + #Px1 + ') THEN 1 ELSE 0
END AS CPT_' + #px1 + '
, a.DX1
, a.DX2
, a.DX3
, a.DX4
FROM ENROLLMENT as b
left join CLAIMS as a
on a.MEMBERID = b.MEMBERID'
EXEC sp_excutesql #sql;
QUIT;
Your issue here is the quotes in the macro variable.
%let Px1='12345';
So now SAS is seeing this:
... THEN 1 ELSE 0 END AS CPT_+'12345'
That's not remotely legal! You need to remove the '.
%let Px1 = 12345;
Then add back on at the right spot.
CASE WHEN a.procedure = "&px1." THEN 1 ELSE 0 END AS CPT_&px1.
Note " not ' as that lets the macro variable resolve.
If you have a list it might help to put the list into a table. Then you can use SAS code to generate the code to make the flag variables instead of macro code.
Say a table with PX code variable.
data pxlist;
input px $10. ;
cards;
12345
4567
;
You could then use PROC SQL query to generate code to make the flag variable into a macro variable.
proc sql noprint;
select catx(' ','PROCEDURE=',quote(trim(px)),'as',cats('CPT_',px))
into :flags separated by ','
from pxlist
;
%put &=flags;
quit;
Code looks like
PROCEDURE= "12345" as CPT_12345,PROCEDURE= "4567" as CPT_4567
So if we make some dummy data.
data enrollment ;
length memberid $8 enrol_mn $6 ;
input memberid enrol_nm;
cards;
1 201612
;
data claims;
length memberid $8 procedure $10 dx1-dx4 $10 ;
input memberid--dx4 ;
cards;
1 12345 1 2 . . .
1 345 1 2 3 . .
;
We can then combine the two tables and create the flag variables.
proc sql noprint;
create table want as
select *,&flags
from ENROLLMENT
natural join CLAIMS
;
quit;
Results
memberid procedure dx1 dx2 dx3 dx4 enrol_mn CPT_12345 CPT_4567
1 12345 1 2 201612 1 0
1 345 1 2 3 201612 0 0

Unable to get parameter passed to macro to resolve as a macro variable

I need to produce the report from the sas dataset t_final_Summary. When I use
the proc report or proc tabulate I won't get the rows for those where column
does not have the value in them. So, I created a new dataset named Expiring
with same columns as in the report.
The t_final_Summary has columns named--Sub_LOB,Group,Mat_Month and Comm_Incl.
I am trying to pass the value of the macros from that table to another table
named Expiring which has the columns named Sub_LOB, Group,Sum_of_Comm_Incl for
varying Mat_month. I wrote the following code:
%macro mat (sub,grp,mth,MCRO);
proc sql no print;
select case
when Sum(Comm_incl)=. then 0
else sum(Comm_Incl)
end format=16.2
into :&MCRO.
from t_final_Summary
where Sub_LOB= "&sub."
and Group="&grp."
and Mat_Month="&mth.";
quit;
%mend mat;
%mat(CRE Commercial, Carolina Group, _Expired, goid);
/*Now I want to check my macro variable goid using %put*/
%put &goid;
Then the log always tells me that the macro goid is not resolved. Is there any
error in the macro statements.
I could not figure out. Can anyone help me please?
Since the rest of your macro seems correct, I am only addressing how to get a parameter to resolve as a macro variable. The macro variable needs to be made global prior to passing it to the macro.
Code:
%Macro Testit(received_var);
Proc sql noprint;
Select make into :&received_var from sashelp.cars where make="Acura";
Quit;
%Put Received_Var(Local): &&&received_var;
%mend;
%Global Passed_Var;
%Testit(passed_var);
%Put Passed_Var(Global) : &passed_var;
Log:
24 %Macro Testit(received_var);
25
26 Proc sql noprint;
27 Select make into :&received_var from sashelp.cars where Make="Acura";
28 Quit;
29
30 %Put Received_Var(Local): &&&received_var;
31
32 %mend;
33
34 %Global Passed_Var;
35 %Testit(passed_var);
NOTE: PROCEDURE SQL used (Total process time):
real time 0.00 seconds
cpu time 0.00 seconds
Received_Var(Local): Acura
36
37 %Put Passed_Var(Global) : &passed_var;
Passed_Var(Global) : Acura

Proc Sql doesn’t execute the 'order by' line when creating a new table in Teradata

I'm trying to use SAS (via SAS EG 4.3) to create a new table in Teradata from an existing sas dataset. The existing sas data set is sorted as needed.
The code will run ok with no errors, and a new table is created in Teradata but it does not execute the order by line in the code . If I set the trace option and look in the sas log the 'orderby ..' line doesn't show in the trace.
The TD table is not ordered as needed , any ideas ?
Regards
sas code
%include "$HOME/tdpp_5200.sas";
options SASTRACE=',,,d' SASTRACELOC=SASLOG MLOGIC MPRINT;
%let Teradata_db = U_DOATDB;
%let Teradata_tb = TBL_AS2_AUDIT_AGG_18MTH;
%let primary_key = 'primary index(CAT)';
libname tdata &rdbms &dbc_info database=&Teradata_db.;
libname datalib "/wload/ar3p/gpfs/teamproj/intr/Projects/AS2_CONTROL_RPT/";
proc sql;
create table tdata.&Teradata_tb. (FASTLOAD=yes dbcreate_table_opts= primary_key) as
select * from datalib.tbl_AS2_audit_agg_18mth
order by AS_YEAR, AS2_MONTH, EVENT_TYPE, RESULT_TYPE,REASON_TYPE, OPERATOR_TYPE;
Quit;
libname tdata clear;
libname datalib clear;
SAS Log
0 1475134126 trprep 0 SQL (2)
TERADATA_0: Prepared: on connection 1 1 1475134126 trprep 0 SQL (2)
SELECT * FROM U_DOATDB."TBL_AS2_AUDIT_AGG_18MTH" 2 1475134126 trprep 0 SQL (2)
3 1475134126 trprep 0 SQL (2)
TERADATA: trforc: COMMIT WORK 4 1475134126 trforc 0 SQL (2)
NOTE: SAS variable labels, formats, and lengths are not written to DBMS tables.
5 1475134132 trexec 0 SQL (2)
TERADATA_1: Executed: on connection 2 6 1475134132 trexec 0 SQL (2)
CREATE MULTISET TABLE U_DOATDB."TBL_AS2_AUDIT_AGG_18MTH" ("CAT" CHAR (200),"AS_Year" DECIMAL(11),"As2_Month"
DECIMAL(11),"EVENT_TYPE" INTEGER,"RESULT_TYPE" CHAR (1),"REASON_TYPE" INTEGER,"OPERATOR_TYPE" CHAR (8),"VOL" FLOAT) primary
index(CAT);COMMIT WORK 7 1475134132 trexec 0 SQL (2)
8 1475134132 trexec 0 SQL (2)
Teradata does not support ordering of data in tables, so SAS removed the ORDER BY clause when passing the code to Teradata. Otherwise the code would have failed.
When reading the data back into SAS you can use any variables you want on a BY statement and SAS will tell Teradata to order the data on the way out of the table.
proc print data=datalib.tbl_AS2_audit_agg_18mth;
by AS_YEAR AS2_MONTH EVENT_TYPE RESULT_TYPE REASON_TYPE OPERATOR_TYPE;
run;