BookmarkSubscribeRSS Feed
Mohan03
Fluorite | Level 6

Hi,

 

Firstly, am using SAS (ver - 9.4) on Mainframes.

 

Secondly on the issue, I used Proc SQL to get some data from a Mainframe Dataset, then used a SET statement to filter some criteria. Now that being done, I have 2 variables (RDE_LEN & RDE_ALL). RDE is just a segment in simple terms and RDE_LEN has the whole length of the RDE_ALL (which has multiple segments like N1, N2 etc with different lengths).

 

I used the traditional SAS code to break the segments and get the values as needed. Now I am trying to do it via a Macro as this segment break happens at multiple places of the program.

 

So when I try to pass the RDE length value, say 3000 bytes, am getting the Variable name i.e. RDE_LEN instead of 3000.

Not sure where I made the mistake (plus I am new to Macros).

 

I have attached the all the code snippets below , pls have a look and advise. Also if you need more info, please revert, will provide them.

 

Thanks,

Mohan

 

Code snippets:

 

Current working SAS code without Macro:

 

DATA BASE001;
SET QUERY001;
WHERE SUBSTR(INTMSGID,1,3) IN ('A01', 'A02', 'A04');
CNT = 1;
DO UNTIL(CNT >= RDE_LEN);
SEG_HEX = PUT(SUBSTR(RDE_ALL,CNT,2),HEX4.);
SEG_LEN = INPUT(SEG_HEX,HEX4.);
SEG_RDE = SUBSTR(RDE_ALL,CNT,SEG_LEN);
SEG_NAM = SUBSTR(SEG_RDE,3,2);
CNT = CNT + 2 + SEG_LEN;
SELECT;
WHEN (SEG_NAM = 'N1') DO;
N1_FBK = SUBSTR(SEG_RDE,007,004);
N1_DCCI = SUBSTR(SEG_RDE,011,001);
N1_EMVPOS = PUT(SUBSTR(SEG_RDE,012,001),HEX2.);
N1_EMVEXP = SUBSTR(SEG_RDE,013,006);
N1_BMAPS = PUT(SUBSTR(SEG_RDE,021,003),HEX6.);
N1_FCR = SUBSTR(SEG_RDE,024,001);
END;
OTHERWISE;
END;
END;

 

Macro definition:

 

********************************* Top of Data **********************************
options symbolgen mprint mlogic;
%macro _brk_seg(lngth);
%put &lngth;
%mend _brk_seg;
******************************** Bottom of Data ********************************

 

Macro Invocation:

 

DATA BASE001;
SET QUERY001;
WHERE SUBSTR(INTMSGID,1,3) IN ('A01', 'A02', 'A04');
%_brk_seg(RDE_LEN);
RUN;

 

MLOGIC Output:

 

MLOGIC(_BRK_SEG): Beginning execution.
MLOGIC(_BRK_SEG): Parameter LNGTH has value RDE_LEN
MLOGIC(_BRK_SEG): %PUT &lngth
SYMBOLGEN: Macro variable LNGTH resolves to RDE_LEN
RDE_LEN
MLOGIC(_BRK_SEG): Ending execution.

 

13 REPLIES 13
PaigeMiller
Diamond | Level 26
%_brk_seg(RDE_LEN);

You are assigning a text string to the macro argument &LNGTH. That text string is 7 characters long, and is these 7 characters: RDE_LEN

 

SAS doesn't know you have other meaning intended for this text string.

 

Perhaps (it's not really clear to me) you mean

 

%let rde_len=3000;


DATA BASE001;
SET QUERY001;
WHERE SUBSTR(INTMSGID,1,3) IN ('A01', 'A02', 'A04');
%_brk_seg(&rde_len)
run;

 

--
Paige Miller
Mohan03
Fluorite | Level 6

Hi,

Thanks for that, let me try to explain my best again.

 

Ok, l tweaked a bit (added a Proc SQL statement) in the Macro invocation section and got the RDE_LEN value into a host variable (snippet below) and then invoked the Macro from there and it got me the result as expected.

 

But I am trying to achieve this by avoiding an extra Proc SQL step, i.e. in the Data step (DATA BASE001) itself which would help in performance as the dataset is huge.

 

Macro Invocation:

DATA BASE001;
SET QUERY001;
WHERE SUBSTR(INTMSGID,1,3) IN ('A01', 'A02', 'A04');
%_brk_seg(RDE_LEN);
RUN;

called the below after the Proc SQL:

PROC SQL;
SELECT RDE_LEN
INTO :WS_LEN
FROM BASE001
QUIT;
%_brk_seg(&WS_LEN);

 

MLOGIC excerpt:

MLOGIC(_BRK_SEG): Beginning execution.
SYMBOLGEN: Macro variable WS_LEN resolves to 433
MLOGIC(_BRK_SEG): Parameter LNGTH has value 433
MLOGIC(_BRK_SEG): %PUT &lngth
SYMBOLGEN: Macro variable LNGTH resolves to 433
433
MLOGIC(_BRK_SEG): Ending execution.

 

Thanks,

Mohan.

PaigeMiller
Diamond | Level 26

I do not understand the relationship between your DATA step and your PROC SQL.

--
Paige Miller
Mohan03
Fluorite | Level 6

The DATA step has a subset of rows from a huge file with around 100 columns (Input variables) and the RDE_LEN is one of it.

 

something like this:

DATA TODAY;
INPUT ID RDE_LEN RDE_ALL;
CARDS;
0001 433 N1_seg+N1_data_of_433_bytes
0001 900 N2_seg+N2_data_of_900_bytes
0001 099 N3_seg+N3_data_of_099_bytes etc

I need the value of RDE_LEN (i.e. 433 in the first instance) and pass it to a Macro to do some process. When I try to do it without the Proc SQL, I was getting the variable name RDE_LEN instead of its value 433.

 

But when I used Proc SQL, am getting the value of 433. So other than this, there is no relation between DATA step and Proc SQL.

 

Hope its clear and the question is, can these values be gotten from the DATA step instead of additional Proc SQL ?

 

Thanks,

Mohan

PaigeMiller
Diamond | Level 26

Given the data set you show, do you want to do this three times? Once when RDE_LEN is 433, and once when RDE_LEN is 900, and once when RDE_LEN is 099? (And if the data set has four or more rows, whould you want to do it once for each row?)

 

Then what? Suppose you have now a macro variables that has value 433, do you then run a macro with that value? And then run it again when the macro variable has value 900? And so on?

 

Really, the whole process isn't clear to me, and we see the trees, but we don't see the forest.

 

I'm going to take a step back here, and insist that you show us the desired code, without macros and without macro variables, for two cases, one when RDE_LEN in 433 and one when RDE_LEN is 900. That's the best way to move forward, and really, its the best way to develop macros by starting from working code that doesn't have macros and doesn't have macro variables.

--
Paige Miller
Mohan03
Fluorite | Level 6

That is correct.

 

For every row, the length of the segment is taken and passed to the Macro where it will break down the data part of the segment into its own smaller fields, like below:

 

WHEN (SEG_NAM = 'N1') DO;
N1_FBK = SUBSTR(SEG_RDE,007,004);
N1_DCCI = SUBSTR(SEG_RDE,011,001);
N1_EMVPOS = PUT(SUBSTR(SEG_RDE,012,001),HEX2.);
N1_EMVEXP = SUBSTR(SEG_RDE,013,006);
N1_BMAPS = PUT(SUBSTR(SEG_RDE,021,003),HEX6.);
N1_FCR = SUBSTR(SEG_RDE,024,001);
END;
OTHERWISE;
END;

 

That part, I can get the count of the records and iterate through the rest of the rows (if the first row is processed successfully). But was struck in the very first row while passing the value to Macro, so to keep it simple, didn't mention the iteration part. 

 

Thanks,

Mohan

Mohan03
Fluorite | Level 6

Below is the current working SAS code without Macro (highlighted in blue) and it works as expected. And since the RDE needs to be broken down at multiple segments, currently am using the "without Macros" SAS code. So want to make the blue piece a Macro defined first and where needed, I can just pass the RDE_LEN & RDE_ALL (data part) and break them into smaller units and get them for report.

 

DATA BASE001;
SET QUERY001;
WHERE SUBSTR(INTMSGID,1,3) IN ('A01', 'A02', 'A04');
CNT = 1;
DO UNTIL(CNT >= RDE_LEN);
SEG_HEX = PUT(SUBSTR(RDE_ALL,CNT,2),HEX4.);
SEG_LEN = INPUT(SEG_HEX,HEX4.);
SEG_RDE = SUBSTR(RDE_ALL,CNT,SEG_LEN);
SEG_NAM = SUBSTR(SEG_RDE,3,2);
CNT = CNT + 2 + SEG_LEN;
SELECT;
WHEN (SEG_NAM = 'N1') DO;
N1_FBK = SUBSTR(SEG_RDE,007,004);
N1_DCCI = SUBSTR(SEG_RDE,011,001);
N1_EMVPOS = PUT(SUBSTR(SEG_RDE,012,001),HEX2.);
N1_EMVEXP = SUBSTR(SEG_RDE,013,006);
N1_BMAPS = PUT(SUBSTR(SEG_RDE,021,003),HEX6.);
N1_FCR = SUBSTR(SEG_RDE,024,001);
END;
OTHERWISE;
END;
END;

 

Below is the SAS code with Macro (not a working one though):

%MACRO _FMTRDE(LNGTH,DATA);
%LET CNT = 1;
DO UNTIL(&CNT >= &LNGTH);
%LET SEG_HEX = PUT(SUBSTR(&DATA,&CNT,2),HEX4.);
%LET SEG_LEN = INPUT(&SEG_HEX,HEX4.);
%LET SEG_RDE = SUBSTR(&DATA,&CNT,&SEG_LEN);
%LET SEG_NAM = SUBSTR(&SEG_RDE,3,2);
%LET CNT = %EVAL(&CNT + 2 + %SEG_LEN);
%PUT &SEG_NAM;

.... Substrings to break further
END;
%MEND _FMTRDE;

 

PaigeMiller
Diamond | Level 26

So, earlier you posted a data step that creates a data set named TODAY, containing values for RDE_LEN. 


Then you want to use these values in the code you just presented that creates data set BASE001, sequentially, so the first time you execute the code to create BASE001 you use RDE_LEN = 433. Then you create BASE001 (or perhaps it has a different name?) with RDE_LEN=900, and so on? Is that it?

 

The overall method is described here: https://communities.sas.com/t5/SAS-Programming/Run-macro-for-values-in-data-set/m-p/707193#M217118

 

I think your macro has to contain the DATA statement and an entire data step, beginning with

 

%macro dothis(rde_len);
DATA BASE001;
    SET QUERY001;
    WHERE SUBSTR(INTMSGID,1,3) IN ('A01', 'A02', 'A04');
    CNT = 1;
    DO UNTIL(CNT >=  &RDE_LEN);

and obviously there's more to that DATA step. And you probably also want the data set created the second time to be named BASE002, and so on.

 

But really, I still don't think I have the right understanding of your task, and it might be worthwhile for you to take a step back, and re-write the whole thing from the beginning, to generate a complete and clear problem statement, as if you are about to present this to a college professor who is going to grade you.

--
Paige Miller
PhilC
Rhodochrosite | Level 12

Learning macros forces us to learn more about how SAS (and other programming languages) process these different types of variables.  First when SAS runs your code it runs in phases.  Long story short, there's a "macro preprocessor" phase (see note) where the macro variables and macro functions are defined and applied to the code.  In later phases, the "compile and execution" phases, the data steps and procedures are compiled and then ran. 

 

When the macro function you created runs, it "SAS" can't know that RDE_LEN is a variable in the data step, nor can it know any of its future values.  SAS only knows that RDE_LEN is a variable in the data step during the compile phase, where, then, it assigns 3000 to the RDE_LEN.

 

So what @PaigeMiller wrote is true, I think I'm filling in some of the holes in the story.  We try to explain things as simply as we need to.  I hope I hit that target with this.

 

note: excuse me, there might be another term for the "macro preprocessor" phase.  I have searched but I can't find where I learned this.  I think I remember seeing a SAS tutorial explaining it, but then I could have seen the same in a C programming class in college. 

Tom
Super User Tom
Super User

I don't get how macro code is going to help you here.  What part of the code do you want to use the macro variable to generate?

 

You seem to be saying that the upper bound for CNT needs to be dynamic in some way, but your current code is already dynamic.  It is referencing a variable RDE_LEN that can have a different value on each observation read from the QUERY001 dataset.

 

Do you need join two tables to get the right values of RDE_LEN attached to the right observations in QUERY001?

Mohan03
Fluorite | Level 6

Ah no, nothing to do with the join to manipulate the RDE_LEN.

 

As I mentioned, am new to the Macros in SAS. So just trying to work out the redundancy of the same executable codes in different places using Macros (seems am looking in for UDF but ended up with Macros).

 

Thanks for your comment.

Mohan03
Fluorite | Level 6

Think I got what I needed from this discussion with you experts. 

 

Will take your valuable inputs from here, try and work out out my best on my requirements. Will seek your advise via another thread for any other expert inputs (probably may come back for the some tuning recommendations).

 

Thanks @PaigeMiller @PhilC @Tom  for your time and expertise. Really appreciate your help.

 

Oops ! Not sure how to close this discussion (if I can), so just leaving (or advise me how, will action on it).

 

Thanks,

Mohan

PaigeMiller
Diamond | Level 26

@Mohan03 wrote:

Think I got what I needed from this discussion with you experts. 

 

Will take your valuable inputs from here, try and work out out my best on my requirements. Will seek your advise via another thread for any other expert inputs (probably may come back for the some tuning recommendations).

 

Thanks @PaigeMiller @PhilC @Tom  for your time and expertise. Really appreciate your help.

 

Oops ! Not sure how to close this discussion (if I can), so just leaving (or advise me how, will action on it).

 

Thanks,

Mohan


That's good to know. There is no need to "close" the discussion, it stays open in case others want to contribute.

--
Paige Miller

sas-innovate-2024.png

Available on demand!

Missed SAS Innovate Las Vegas? Watch all the action for free! View the keynotes, general sessions and 22 breakouts on demand.

 

Register now!

How to Concatenate Values

Learn how use the CAT functions in SAS to join values from multiple variables into a single value.

Find more tutorials on the SAS Users YouTube channel.

Click image to register for webinarClick image to register for webinar

Classroom Training Available!

Select SAS Training centers are offering in-person courses. View upcoming courses for:

View all other training opportunities.

Discussion stats
  • 13 replies
  • 2215 views
  • 3 likes
  • 4 in conversation