/*** TIP 00391 ***/

  /*****************************************************************/
  /***MYEXCEL.SAS                                                ***/
  /***   SAS Macro to dump the contents of a SAS Data Set to a   ***/
  /***       CSV File for input to an Excel Spread Sheet         ***/
  /***       using existing SAS Formats to format output         ***/
  /***                                                           ***/
  /***   Macro Vars                                              ***/
  /***       LIBDSN   = SAS Dataset name ( CFDATA.AAAlines)      ***/
  /***       DOWNLOAD = CSV File ("userdisk1:[name]aaalines.csv")***/
  /***       VARNLABL = Y or N (Place Var Names and Labels on    ***/
  /***                          1st and 2nd line of CSV File)    ***/
  /***       MOD      = MOD (Need to Append output to existing   ***/
  /***                        CSV File)                          ***/
  /*****************************************************************/

  %MACRO MYEXCEL(LIBDSN,DOWNLOAD, VARNLABL, MOD);
   FILENAME OUTFILE &DOWNLOAD LRECL=8000 RECFM=V ;
   PROC CONTENTS DATA=&LIBDSN
   OUT=_TEMP_(KEEP=NAME TYPE VARNUM LABEL FORMAT FORMATD FORMATL) NOPRINT;
   RUN;

   PROC SORT DATA=_TEMP_ OUT=_TEMP_; BY VARNUM; RUN;

   DATA _NULL_; SET _TEMP_ END=EOF;
    CALL SYMPUT('ZVR'||(LEFT(PUT(_N_,5.))),NAME);
    CALL SYMPUT('TYP'||(LEFT(PUT(_N_,5.))),LEFT(PUT(TYPE,8.)));
    IF LABEL = ' ' THEN LABEL = NAME;
    CALL SYMPUT('LBL'||(LEFT(PUT(_N_,5.))),LEFT(LABEL));
    CALL SYMPUT('FMT'||(LEFT(PUT(_N_,5.))),LEFT(FORMAT));
    CALL SYMPUT('FMD'||(LEFT(PUT(_N_,5.))),PUT(FORMATD,BEST.));
    CALL SYMPUT('FML'||(LEFT(PUT(_N_,5.))),PUT(FORMATL,BEST.));
    IF EOF THEN CALL SYMPUT('TOTAL',LEFT(PUT(_N_,8.)));
   RUN;

  DATA _NULL_;
   %DO ZI=1 %TO &TOTAL;
     LENGTH  TMP&ZI $40 ;
     TMP&ZI = "&&FMT&ZI" || "&&FML&ZI" || "." || "&&FMD&ZI";
     if compress("&&FML&ZI") = "0" then do;
       TMP&ZI = "&&FMT&ZI" || ".";
     end;
     TMP&ZI = COMPRESS(TMP&ZI);
     IF "&&FMT&ZI" = " " THEN TMP&ZI = 'BEST.' ;
     CALL SYMPUT("FMT&ZI", TMP&ZI);
   %END;
  RUN;

   DATA _NULL_;
    FILE OUTFILE  NOPRINT NOTITLES &MOD;
    SET &LIBDSN;
    FORMAT _NUMERIC_ BEST12.;
     IF _N_ = 1 THEN PUT 'SEP=!';
    %IF &VARNLABL EQ Y %THEN %DO;
       IF _N_ = 1 THEN DO;
        PUT
       %DO YI =1 %TO &TOTAL;
         %CMPRES("'&&ZVR&YI") +(1) '!'
       %END;
       +(-1)' '; /* REMOVE LAST HANGING COMMA DELIMITER */
      END;
       IF _N_ = 1 THEN DO;
        PUT
       %DO XI =1 %TO &TOTAL;
         %CMPRES("'&&LBL&XI") +(1) '!'
       %END;
       +(-1)' '; /* REMOVE LAST HANGING COMMA DELIMITER */
      END;
    %END;
    PUT
     %DO WI=1 %TO &TOTAL;
       %IF &&TYP&WI=1 %THEN %DO;  /* IF NUMERIC VARIABLE */
           &&ZVR&WI  &&FMT&WI +(1) '!'
       %END;
       %ELSE %DO;   /* IF CHARACTER VARIABLE */
          " "  &&ZVR&WI  +(-1) "!"
       %END;
     %END;
     +(-1) ' '; /* REMOVE THE EXTRA COMMA AT THE END */
   RUN;
   %MEND MYEXCEL;

 /*** end of program - MYEXCEL ***/

 /*** sample call to MACRO ***/

  *** %myexcel( cfdata.aaalines , "aaalines.csv", N, );
  *** %myexcel( cfdata.aaalines , "aaalines.csv", N, );

Solution #2 by Lars L Jacobsen %MACRO MakeCSV(DSet, OUT, delim=',', limit=, label=NAME, title=); /*---------------------------------------------------------------------------------*/ /* Exports dataset to delimited file */ /* Parametres: */ /* DSet : Name of dataset - mandatory. Accepts datasetoptions like KEEP, DROP */ /* Out : Name of outputfile - mandatory. Enclose in single quotes */ /* delim : Delimiter character - optional. Default = ',' (comma) */ /* limit : Limit to this number of OBS - optional */ /* label : What to put in first row [LABEL|NAME|NONE] */ /* default: NAME */ /* title : An additional row with title - optional. */ /*---------------------------------------------------------------------------------*/ /* Save NOTES option and turn it off */ %LET notes=%SYSFUNC(GETOPTION(NOTES, KEYWORD)); OPTIONS NONOTES; /* Put DSet's structure in dataset: _TEMP_ */ PROC CONTENTS DATA=&DSet OUT=_TEMP_(KEEP=NAME LABEL TYPE VARNUM) NOPRINT; RUN; PROC SORT DATA=_TEMP_ OUT=_TEMP_; BY VARNUM; RUN; DATA _TEMP_; SET _TEMP_ END=EOF; IF LABEL = '' THEN LABEL = NAME; CALL SYMPUT('VAR'||(LEFT(PUT(_N_,5.))),NAME); CALL SYMPUT('TYP'||(LEFT(PUT(_N_,5.))),LEFT(PUT(TYPE,8.))); IF EOF THEN CALL SYMPUT('TOTAL',LEFT(PUT(_N_,8.))); RUN; /* Prepare for header/title */ DATA _NULL_; CALL SYMPUT('keep', 'name'); %IF %UPCASE(&label)=LABEL %THEN CALL SYMPUT('keep', 'label'); ; RUN; /* Put Fieldnames/Labels in same OBS */ PROC TRANSPOSE DATA=_TEMP_(KEEP=&&keep) OUT=_TEMP2_(DROP=_NAME_ _LABEL_) PREFIX=FELT ; VAR &&keep ; RUN ; /* This step is for the header/title */ DATA _NULL_; FILE &OUT NOPRINT; SET _TEMP2_; IF _N_ = 1 THEN PUT 'SEP=' &delim; %IF &title NE %THEN PUT "&title" ; ; %IF NOT (%UPCASE(&label)=NONE) %THEN %DO; PUT %DO I=1 %TO &TOTAL; %IF &&TYP&I=1 %THEN %DO; /* If it is a numeric variable */ FELT&I +(-1) &delim %END; %ELSE %DO; /* If it is a character variable */ '"' FELT&I +(-1) '"' &delim %END; %END; +(-1) ' '; /* Remove the extra delimiter at the end of the line */ %END; RUN; /* We want NOTES for the actual data write, so restore it if it was initially on */ OPTIONS ¬es; /* This step is for the actual data */ DATA _NULL_; FILE &OUT NOPRINT MOD; SET &DSet; * FORMAT _NUMERIC_ BEST12.; %IF &limit gt 0 %THEN %DO ; IF _N_ gt &limit THEN STOP ; %END ; PUT %DO I=1 %TO &TOTAL; %IF &&TYP&I=1 %THEN %DO; /* If it is a numeric variable */ &&VAR&I +(-1) &delim %END; %ELSE %DO; /* If it is a character variable */ '"' &&VAR&I +(-1) '"' &delim %END; %END; +(-1) ' '; /* Remove the extra delimiter at the end of the line */ RUN; OPTIONS NONOTES; PROC DATASETS NOLIST; DELETE _TEMP_; DELETE _TEMP2_; QUIT; OPTIONS ¬es; %MEND MakeCSV; /*** end of tip 00391 ***/