Search code examples
sassas-macro

SAS: Dynamically determine input fields from a dataset


I am trying to parse a delimited dataset with over 300 fields. Instead of listing all the input fields like

    data test;
    infile "delimited_filename.txt"
            DSD delimiter="|" lrecl=32767 STOPOVER;

    input   field_A:$200.
            field_B :$200.
            field_C:$200.
            /*continues on */
    ;

I am thinking I can dump all the field names into a file, read in as a sas dataset, and populate the input fields - this also gives me the dynamic control if any of the field names changes (add/remove) in the dataset. What would be some good ways to accomplish this?

Thank you very much - I just started sas, still trying to wrap my head around it.


Solution

  • This worked for me - Basically "write" data open code using macro language and run it.

    Note: my indata_header_file contains 5 columns: Variable_Name, Variable_Length, Variable_Type, Variable_Label, and Notes.

    %macro ReadDsFromFile(filename_to_process, indata_header_file, out_dsname);
    
    %local filename_to_process indata_header_file out_dsname;
    
    /* This macro var contain code to read data file*/
    %local read_code input_in_line; 
    %put *** Processing file: &filename_to_process ...;
    
    /* Read in the header file */
    proc import OUT     = ds_header
            DATAFILE    = &indata_header_file.
            DBMS        = EXCEL REPLACE;        /* REPLACE flag */
            SHEET       = "Names";
            GETNAMES    = YES;
            MIXED       = NO;
            SCANTEXT    = YES; 
    run;
    
    %let id     = %sysfunc(open(ds_header));
    %let NOBS   = %sysfunc(attrn(&id.,NOBS)); 
    %syscall set(id); 
    
    /* 
        Generates:
        data &out_dsname.;
        infile "&filename_to_process."
            DSD delimiter="|" lrecl=32767 STOPOVER FIRSTOBS=3;  
            input   
            '7C'x
    */
    
    %let read_code = data &out_dsname. %str(;)
                            infile &filename_to_process.
                            DSD delimiter=%str("|") lrecl=32767 STOPOVER %str(;)
                            input ;
    
    /*
        Generates:
        <field_name> : $<field_length>;
    */
    %do i = 1 %to &NObs;
        %let rc             = %sysfunc(fetchobs(&id., &i));
        %let VAR_NAME       = %sysfunc(getvarc(&id., %sysfunc(varnum(&id., Variable_Name)) ));  
        %let VAR_LENGTH     = %sysfunc(getvarn(&id., %sysfunc(varnum(&id., Variable_Length)) ));    
        %let VAR_TYPE       = %sysfunc(getvarc(&id., %sysfunc(varnum(&id., Variable_Type)) ));  
        %let VAR_LABEL      = %sysfunc(getvarc(&id., %sysfunc(varnum(&id., Variable_Label)) ));
        %let VAR_NOTES      = %sysfunc(getvarc(&id., %sysfunc(varnum(&id., Notes)) ));      
    
        %if %upcase(%trim(&VAR_TYPE.)) eq CHAR %then 
            %let input_in_line = &VAR_NAME :$&VAR_LENGTH..;
        %else
            %let input_in_line = &VAR_NAME :&VAR_LENGTH.;
    
        /* append in_line statment to main macro var*/
        %let read_code = &read_code. &input_in_line. ;
    %end; 
    
    /* Close the fid */
    %let rc = %sysfunc(close(&id));
    
    %let read_code = &read_code. %str(;) run %str(;) ;
    
    /* Run the generated code*/
    &read_code.
    
    %mend ReadDsFromFile;