Send As SMS

Friday, January 19, 2007

Making Variable Values Private in SQL

Here's another usage of the file info functions. I wrote this because I wanted to display claims records on a web page, but without any personal information. Various data sets might be displayed, and they might not all have all of the variables I wanted to make private. I didn't want to write a separate SQL statement for each of the dozens of data sets, so I wrote a utility macro.

Task: Given a data set which contains soc_sec_num and other variables, create a select clause which masks soc_sec num but displays the other variables normally. Extend that to an arbitrary number of privatized variables and a large number of data set variables.

Here's my solution:

%macro mtPrivatizeSqlVars(
      data=,
      privatevars=soc_sec_num prime_prsn_ssn last_nm first_nm,
      newvalues=0 0 Lastname Firstname);
/* 10-Dec-2004 Jack Hamilton                                   */
/* Macro:      mtPrivatizeSqlVars                              */
/*                                                             */
/* Purpose:    Create a list of variables in a data set,       */
/*    separated by commas and suitable for use in an SQL       */
/*    SELECT statement, where values of specified vars are     */
/*    replaced by a fixed string.                              */
/*                                                             */
/* Parameters:                                                 */
/*    Data - the name of the dataset (libref.member)           */
/*    PrivateVars - list of variables to be privatized,        */
/*             separated by spaces                             */
/*    NewValues - values to be used instead of private values  */
/*                                                             */

   %local i j dsid nvars varlist thisvarname lowvarname varlabel vartype privatewordpos newtext thisword;

   %let privatevars = %lowcase(&PRIVATEVARS.);

   %let varlist = ERROR IN mtPrivatizeSqlVars MACRO;

   /* Open the dataset.  */

   %let dsid = %SYSFUNC(open(&DATA., I));
   %if &DSID = 0 %then
      %do;
      %let varlist = ERROR IN mtPrivatizeSqlVars MACRO: %sysfunc(sysmsg());
      %put ERROR: &VARLIST.;
      %goto EXIT;
      %end;

   %let nvars = %SYSFUNC(attrn(&DSID., nvars));
   %let varlist = ;

   %do i = 1 %to %EVAL(&NVARS. - 1);
      %let thisvarname = %SYSFUNC(varname(&DSID. ,&I.));
      %let lowvarname = %LOWCASE(&thisvarname);
      %let privatewordpos = 0;

      /* Find location of varname in private list. */
      %do j = 1 %to 999;
         %let thisword = %lowcase(%scan(&PRIVATEVARS., &J., %str( )));
         %if "&THISWORD." = "" %then %goto nomorewords;
         %if "&THISWORD." = "&LOWVARNAME." %then
            %do;
            %let privatewordpos = &J.;
            %goto nomorewords;
            %end;
      %end;
      %NOMOREWORDS:

      %if &PRIVATEWORDPOS. = 0 %then
         %let varlist = &VARLIST. &THISVARNAME. ,;
      %else
         %do;
         %let varlabel = %SYSFUNC(varlabel(&DSID. ,&I.));
         %let vartype = %SYSFUNC(vartype(&DSID. ,&I.));
         %let newtext = %scan(&NEWVALUES., &PRIVATEWORDPOS., %str( ));
         %if "&VARLABEL." = "" %then %let varlabel = &THISVARNAME.;
         %if &VARTYPE. = N %then
            %let varlist = &VARLIST. &NEWTEXT. as &THISVARNAME. label="&VARLABEL.", ;
         %else
            %let varlist = &VARLIST. "&NEWTEXT." as &THISVARNAME. label="&VARLABEL.", ;
         %end;
   %end;

   %let thisvarname = %SYSFUNC(varname(&DSID. ,&I.));
   %let lowvarname = %LOWCASE(&thisvarname);
      %if %sysfunc(indexw(&PRIVATEVARS., &LOWVARNAME.)) = 0 %then
         %let varlist = &VARLIST. &THISVARNAME.;
      %else
         %do;
         %let varlabel = %SYSFUNC(varlabel(&DSID. ,&I.));
         %let vartype = %SYSFUNC(vartype(&DSID. ,&I.));
         %let newtext = %scan(&NEWVALUES., &PRIVATEWORDPOS., %str( ));
         %if "&VARLABEL." = "" %then %let varlabel = &THISVARNAME.;
         %if &VARTYPE. = N %then
            %let varlist = &VARLIST. &NEWTEXT. as &THISVARNAME. label="&VARLABEL." ;
         %else
            %let varlist = &VARLIST. "&NEWTEXT." as &THISVARNAME. label="&VARLABEL." ;
         %end;

   /* Close the dataset.                                          */

%EXIT:

   %let rc = %SYSFUNC(close(&DSID.));

   &VARLIST.

%mend mtPrivatizeSqlVars;

data test;
   last_nm = 'Hamilton'; soc_sec_num = 123456789; 
   incrd_da = '12dec2004'd; paid_amnt = 75.00; 
   proc_cd = '99213';
   format incrd_da worddatx12.;
   output;
run;

%let changedvars = %mtprivatizesqlvars(data=test);
%put &CHANGEDVARS.;

proc sql;

   select   &CHANGEDVARS.
   from     test
   ;

quit;

Relevant log:

2023
2024  %let changedvars = %mtprivatizesqlvars(data=test);
2025  %put &CHANGEDVARS.;
"Lastname" as last_nm label="last_nm", 0 as soc_sec_num label="soc_sec_num", incrd_da , paid_amnt , proc_cd

Output:

last_nm   soc_sec_num      incrd_da  paid_amnt  proc_cd
Lastname            0   12 Dec 2004         75  99213

I then use the macro in an htmSQL page. htmSQL works better in this case than a SAS program in the broker would, because it's easy to generate a table with two header rows, one for the variable name and one for the variable label.

0 Comments:

Post a Comment

<< Home