Examples: DATA Step Functions for Reading Metadata

Overview

This section describes how to use SAS metadata DATA step functions to identify and track metadata that describes data libraries and users. The examples show how to:
  • list the SAS libraries that are defined in metadata
  • list the servers that are used to access the libraries
  • list the logins defined on the system and their associated user identities and authentication domains
  • list user group assignments
  • list the logins used by metadata identities
Because these examples do not create metadata, they can be run on a production metadata server. However, they must be executed by a user who has authorization to the metadata, such as the SAS Administrator.

Metadata Access Overview

There is no need to know the physical location of metadata to access it. All access to metadata is controlled by the SAS Metadata Server. To use the metadata server, a program must establish a connection to it. In a SAS program, you establish a connection by specifying metadata system options. For information about the metadata server connection system options, see Overview of System Options for Metadata.
Once a server connection is established, communicating with the metadata server involves defining variables for function arguments and issuing metadata DATA step functions. Use the FORMAT or LENGTH statement to define argument variables. Use the KEEP statement to specify which variables to include in the output data set. You should be familiar with the SAS Metadata Model metadata types that represent entities that you want to query, and the properties defined for each metadata type.
The SAS metadata DATA step functions use a Uniform Resource Identifier (URI) argument to access metadata objects. The preferred URI forms are:
 “omsobj: type/ID”
or
“omsobj: type?@attribute='value'”
type is the name of the metadata type that represents the entity in the SAS Metadata Model. The ID value or attribute= ‘value’ pair is used as a filter to locate the objects that meet the criteria.
In the examples that follow, the following URI is used to request all objects of the specified type:
"omsobj:type?@Id contains '.'"
The URI can return multiple objects. The GETNOBJ DATA step function returns output in a two-dimensional array. Each call to GETNOBJ retrieves the URI representing the nth object returned from the query. In the examples, n=1 specifies to get the first object in the array. n+1 specifies to iterate through the content of the array.

Featured Functions

METADATA_GETNOBJ
Returns the nth object that matches the specified URI. For more information, see METADATA_GETNOBJ Function.
METADATA_GETATTR
Returns the value of the specified attribute for the specified object. For more information, see METADATA_GETATTR Function.
METADATA_GETNASN
Returns the nth associated object of the specified association. For more information, see METADATA_GETNASN Function.
METADATA_RESOLVE
Resolves a URI into an object on the metadata server. For more information, see METADATA_RESOLVE Function.

Featured Metadata Types and Associations

  • A SAS library is described in the SAS Metadata Model by the SASLibrary metadata type. A directory is described by the Directory metadata type. A database schema is described by the DatabaseSchema metadata type. A SASLibrary metadata object has a UsingPackages association to a Directory or DatabaseSchema metadata object.
  • A group of SAS servers of different server types is described in the SAS Metadata Model by the ServerContext metadata type. A SASLibrary metadata object has a DeployedComponents association to a ServerContext metadata object.
  • External logins are represented in the SAS Metadata Model by the Login metadata type. Users are represented by the Person metadata type. User groups are represented by the IdentityGroup metadata type. Person and IdentityGroup are subtypes of the Identity metadata type. An authentication domain is represented by the AuthenticationDomain metadata type. A Login object has an AssociatedIdentities association to objects of the Identity subtypes. A Login object has a Domains association to an AuthenticationDomain object.
  • A Person metadata object has an IdentityGroups association to an IdentityGroup metadata object. A Person metadata object has a Logins association to a Login metadata object.
  • Internal logins are represented by the InternalLogin metadata type. An InternalLogin has a ForIdentity association to an identity. An identity has a InternalLoginInfo association to an InternalLogin.
For more information about the metadata types and their associations, see SAS 9.3 Metadata Model: Reference.

Example: Listing Libraries and Their Associated Directory or Database Schema

This program uses the SAS metadata DATA step functions to query the metadata repository, and return a list of all libraries and their associated directory or database schema. The results are returned to a SAS data set in the Work library, which is printed with PROC PRINT.
Note: When running the program, be sure to modify the META* system options to provide connection parameters for your metadata server. METAUSER should be a user who has ReadMetadata permission to the metadata objects being queried. METAREPOSITORY specifies to look in the Foundation repository. If you have more than one metadata repository, you might want to run this program on all of the repositories. The same is true for other examples in this section.
/*Connect to the metadata server.  */

options metaserver="myserver"
	metaport=8561
	metauser="sasadm@saspw"
	metapass="adminpw"
	metarepository="Foundation";

/* Begin the query. The DATA statement names the output data set.  */

data metadata_libraries;

/* The LENGTH statement defines variables for function arguments and
assigns the maximum length of each variable.  */

  length liburi upasnuri $256 name $128 type id $17 libref engine $8 path 
mdschemaname schema $256;

/* The KEEP statement defines the variables to include in the 
output data set.  */

  keep name libref engine path mdschemaname schema;

/* The CALL MISSING routine initializes the output variables to missing values.  */

  call missing(liburi,upasnuri,name,engine,libref);

  /* The METADATA_GETNOBJ function specifies to get the SASLibrary objects 
in the repository. The argument nlibobj=1 specifies to get the first object that
matches the requested URI. liburi is an output variable. It will store the URI 
of the returned SASLibrary object. */

  nlibobj=1;
  librc=metadata_getnobj("omsobj:SASLibrary?@Id contains '.'",nlibobj,liburi);

  /* The DO statement specifies a group of statements to be executed as a unit
for each object that is returned by METADATA_GETNOBJ. The METADATA_GETATTR function
is used to retrieve the values of the Name, Engine, and Libref attributes of 
the SASLibrary object.  */

  do while (librc>0);

     /* Get Library attributes */
     rc=metadata_getattr(liburi,'Name',name);
     rc=metadata_getattr(liburi,'Engine',engine);
	  rc=metadata_getattr(liburi,'Libref',libref);
	 
	  /* The METADATA_GETNASN function specifies to get objects associated to the 
library via the UsingPackages association. The n argument specifies to return the 
first associated object for that association type. upasnuri is an output variable. 
It will store the URI of the associated metadata object, if one is found.  */

	    n=1;
	    uprc=metadata_getnasn(liburi,'UsingPackages',n,upasnuri);

	    /* When a UsingPackages association is found, the METADATA_RESOLVE function 
is called to resolve the URI to an object on the metadata server. The CALL MISSING 
routine assigns missing values to output variables.  */

	    if uprc > 0 then do;
	       call missing(type,id,path,mdschemaname,schema);
	       rc=metadata_resolve(upasnuri,type,id);

           /* If type='Directory', the METADATA_GETATTR function is used to get its 
path and output the record */

           if type='Directory' then do;
		      rc=metadata_getattr(upasnuri,'DirectoryName',path);
			  output;
              end; 

           /* If type='DatabaseSchema', the METADATA_GETATTR function is used to get 
the name and schema, and output the record */

           else if type='DatabaseSchema' then do;
               rc=metadata_getattr(upasnuri,'Name',mdschemaname);
               rc=metadata_getattr(upasnuri,'SchemaName',schema);
              output;
              end; 

		/* Check to see if there are any more Directory objects */

            n+1;
            uprc=metadata_getnasn(liburi,'UsingPackages',n,upasnuri);
		  end; /* if uprc > 0 */

	 /* Look for another library */

	 nlibobj+1;
     librc=metadata_getnobj("omsobj:SASLibrary?@Id contains '.'",nlibobj,liburi);
  end; /* do while (librc>0) */
run;

/* Print the metadata_libraries data set */ 

proc print data=metadata_libraries; run;
The example creates output similar to the following:
PROC PRINT of metadata_libraries Data Set
PROC PRINT of metadata_libraries Data Set

Example: Listing Libraries and Their Server Contexts

This program uses the SAS metadata DATA step functions to return more detailed information about the libraries. The results are returned to a Libraries data set in the Work library. The requested data includes the library metadata ID, the library name, libref, engine, path on the file system (or if DBMS data, the DBMS path), and the server contexts to which the library is associated.
/*Connect to the metadata server with the metadata system options, 
as shown in the previous example. */
   
data work.Libraries;

/* The LENGTH statement defines variables for function arguments and
assigns the maximum length for each variable.  */

  length LibId LibName $ 32 LibRef LibEngine $ 8 LibPath $ 256
ServerContext uri uri2 type $ 256 server $ 32;

/* The LABEL statement assigns descriptive labels to variables. */

  label
LibId = "Library Id"
LibName = "Library Name"
LibRef = "SAS Libref"
LibEngine = "Library Engine"
ServerContext = "Server Contexts"
LibPath = "Library Path"
;

/* The CALL MISSING routine initializes output variables to missing values.  */

  call missing(LibId,LibName,LibRef,LibEngine,LibPath,
       ServerContext,uri,uri2,type,server);
  n=1;
  n2=1;

  /* The METADATA_GETNOBJ function gets the first Library object. If none 
are found, the program prints an informational message. */
  rc=metadata_getnobj("omsobj:SASLibrary?@Id contains '.'",n,uri);
  if rc<=0 then put "NOTE: rc=" rc 
    "There are no Libraries defined in this repository"
    " or there was an error reading the repository.";

/* The DO statement specifies a group of statements to be executed as a unit
for the object that is returned by METADATA_GETNOBJ. The METADATA_GETATTR 
function gets the values of the Id, Name, LibRef, and Engine attributes 
of the SASLibrary object.  */

  do while(rc>0);
     objrc=metadata_getattr(uri,"Id",LibId);
     objrc=metadata_getattr(uri,"Name",LibName);
objrc=metadata_getattr(uri,"Libref",LibRef);
objrc=metadata_getattr(uri,"Engine",LibEngine);

	 /* The METADATA_GETNASN function gets objects associated 
via the DeployedComponents association. If none are found, the program
prints an informational message. */

	 objrc=metadata_getnasn(uri,"DeployedComponents",n2,uri2);
	 if objrc<=0 then
       do;
         put "NOTE: There is no DeployedComponents association for "
             LibName +(-1)", and therefore no server context.";
	     ServerContext="";
	   end;

 /* When an association is found, the METADATA_GETATTR function gets
the server name. */
 
	   do while(objrc>0);
         objrc=metadata_getattr(uri2,"Name",server);
	     if n2=1 then ServerContext=quote(trim(server));
		 else ServerContext=trim(ServerContext)||" "||quote(trim(server));

/* Look for another ServerContext */
	     n2+1;
         objrc=metadata_getnasn(uri,"DeployedComponents",n2,uri2);
	   end; /*do while objrc*/ 

     n2=1;

	 /* The METADATA_GETNASN function gets objects associated via the 
UsingPackages association. The program prints a message if an 
association is not found.*/

	 objrc=metadata_getnasn(uri,"UsingPackages",n2,uri2);
	 if objrc<=0 then
       do;
         put "NOTE: There is no UsingPackages association for " 
             LibName +(-1)", and therefore no Path.";
	     LibPath="";
	   end;

/* When a UsingPackages association is found, the METADATA_RESOLVE function 
is called to resolve the URI to an object on the metadata server. */

	   do while(objrc>0);
	     objrc=metadata_resolve(uri2,type,id);

	/*if type='Directory', the METADATA_GETATTR function is used to get its path */

	if type='Directory' then objrc=metadata_getattr(uri2,"DirectoryName",LibPath);

	/*if type='DatabaseSchema', the METADATA_GETATTR function is used to get 
the name */

	 else if type='DatabaseSchema' then objrc=metadata_getattr(uri2, "Name", LibPath);
		 else LibPath="*unknown*";

		/* output the records */
	     output;
	     LibPath="";

		/* Look for other directories or database schemas */

         n2+1;
	     objrc=metadata_getnasn(uri,"UsingPackages",n2,uri2);
	   end; /*do while objrc*/ 

     ServerContext="";
     n+1;

		/* Look for other libraries */

	 n2=1;
     rc=metadata_getnobj("omsobj:SASLibrary?@Id contains '.'",n,uri);

  end; /*do while rc*/  

/* The KEEP statement defines the variables to include in the output data set. */

  keep
LibId
LibName
LibRef
LibEngine
ServerContext
LibPath; 
run;

/* Write a basic listing of data */

proc print data=work.Libraries label;
  /* subset results if you wish
     where indexw(ServerContext,'"SASMain"') > 0; */
run;
The example creates output similar to the following:
PROC PRINT of work.Libraries Data Set
PROC PRINT of work.Libraries Data Set

Example: Listing Logins and Their Associated Identities and Authentication Domains

This program uses the SAS metadata DATA step functions to query the metadata repository, and return a list of all logins and the users or groups to which they belong. It returns the authentication domains in which the logins are active. The results are returned to a Logins data set in the Work library.
Note: A typical user can see only logins that he or she owns, and the logins of groups of which he or she is a member. For this example to return meaningful information, it must be executed by an unrestricted user or by a user who has been assigned the User and Group Administrative role.
/*Connect to the metadata server using the metadata system options 
shown in the first example.*/

data logins;

  /* The LENGTH statement defines variables for function arguments and assigns
the maximum length for each variable.  */
  
  length LoginObjId UserId IdentId AuthDomId $ 17
         IdentType $ 32
         Name DispName Desc uri uri2 uri3 AuthDomName $ 256;

/* The CALL MISSING routine initializes the output variables to missing values.  */

  call missing
(LoginObjId, UserId, IdentType, IdentId, Name, DispName, Desc, AuthDomId, AuthDomName);
  call missing(uri, uri2, uri3);
  n=1;

 /* The METADATA_GETNOBJ function specifies to get the Login objects 
in the repository. The n argument specifies to get the first object that
matches the uri requested in the first argument. The uri argument is an output 
variable. It will store the actual uri of the Login object that is returned. 
The program prints an informational message if no objects are found. */

  objrc=metadata_getnobj("omsobj:Login?@Id contains '.'",n,uri);
  if objrc<=0 then put "NOTE: rc=" objrc 
    "There are no Logins defined in this repository"
    " or there was an error reading the repository.";

/* The DO statement specifies a group of statements to be executed as a unit
for the Login object that is returned by METADATA_GETNOBJ. The METADATA_GETATTR 
function gets the values of the object's Id and UserId attributes. */

  do while(objrc>0);
     arc=metadata_getattr(uri,"Id",LoginObjId);
     arc=metadata_getattr(uri,"UserId",UserId);
  
/* The METADATA_GETNASN function specifies to get objects associated 
via the AssociatedIdentity association. The AssociatedIdentity association name 
returns both Person and IdentityGroup objects, which are subtypes of the Identity
metadata type. The URIs of the associated objects are returned in the uri2 variable. 
If no associations are found, the program prints an informational message. */

     n2=1;
     asnrc=metadata_getnasn(uri,"AssociatedIdentity",n2,uri2);
     if asnrc<=0 then put "NOTE: rc=" asnrc 
       "There is no Person or Group associated with the " UserId "user ID.";

/* When an association is found, the METADATA_RESOLVE function is called to 
resolve the URI to an object on the metadata server. */

     else do;
       arc=metadata_resolve(uri2,IdentType,IdentId);

	/* The METADATA_GETATTR function is used to get the values of each identity's 
Name, DisplayName and Desc attributes. */

       arc=metadata_getattr(uri2,"Name",Name);
       arc=metadata_getattr(uri2,"DisplayName",DispName);
       arc=metadata_getattr(uri2,"Desc",Desc);
     end;
  
 /* The METADATA_GETNASN function specifies to get objects associated 
via the Domain association. The URIs of the associated objects are returned in 
the uri3 variable. If no associations are found, the program prints an 
informational message. */ 
  
     n3=1;
     autrc=metadata_getnasn(uri,"Domain",n3,uri3);
     if autrc<=0 then put "NOTE: rc=" autrc 
       "There is no Authentication Domain associated with the " UserId "user ID.";
 
		/* The METADATA_GETATTR function is used to get the values of each 
AuthenticationDomain object's Id and Name attributes. */

     else do;
       arc=metadata_getattr(uri3,"Id",AuthDomId);
       arc=metadata_getattr(uri3,"Name",AuthDomName);
     end;

     output;

  /* The CALL MISSING routine reinitializes the variables back to missing values. */

  call missing(LoginObjId, UserId, IdentType, IdentId, Name, DispName, Desc, AuthDomId, 
AuthDomName);

 /* Look for more Login objects */

 n+1;
  objrc=metadata_getnobj("omsobj:Login?@Id contains '.'",n,uri);
  end;  

/* The KEEP statement specifies the variables to include in the output data set.  */

  keep LoginObjId UserId IdentType Name DispName Desc AuthDomId AuthDomName; 
run;

/* The PROC PRINT statement prints the output data set. */
proc print data=logins;
   var LoginObjId UserId IdentType Name DispName Desc AuthDomId AuthDomName;
run;
The example creates output similar to the following:
PROC PRINT of Logins Data Set
PROC PRINT of Logins Data Set

Example: Listing User Group Memberships

This program uses the SAS metadata DATA step functions to query the metadata repository, and return a list of all users and the user groups to which they belong. The results are returned to a Users_Grps data set in the Work library. The results are presented in a listing created with PROC REPORT.
Note: User groups are represented in the SAS Metadata Model by the IdentityGroup metadata type. The IdentityGroup metadata type is also used to represent roles. This example lists IdentityGroup objects of both types. If you want to exclude roles from the listing, use the METADATA_GETATTR function to get the value of each object’s PublicType attribute. A traditional user group has PublicType= “UserGroup”. A role has PublicType=“Role”. Then, use the values to distinguish between two types of IdentityGroup objects.
/*Connect to the metadata server using the metadata system options as 
shown in the first example. */

data users_grps;

/* The LENGTH statement defines variables for function arguments and 
assigns the maximum length of each variable.  */  

  length uri name dispname group groupuri $256 
id MDUpdate $20;
  
/* The CALL MISSING routine initializes output variables to missing values.*/

  n=1;
	  call missing(uri, name, dispname, group, groupuri, id, MDUpdate);

    
  /* The METADATA_GETNOBJ function specifies to get the Person objects 
in the repository. The n argument specifies to get the first Person object that is
returned. The uri argument will return the actual uri of the Person object that 
is returned. The program prints an informational message if no Person objects 
are found. */

      nobj=metadata_getnobj("omsobj:Person?@Id contains '.'",n,uri);
  if nobj=0 then put 'No Persons available.';

/* The DO statement specifies a group of statements to be executed as a unit
for the Person object that is returned by METADATA_GETNOBJ. The METADATA_GETATTR 
function gets the values of the object's Name and DisplayName attributes. */

  else do while (nobj > 0);
     rc=metadata_getattr(uri, "Name", Name);
     rc=metadata_getattr(uri, "DisplayName", DispName);


/* The METADATA_GETNASN function gets objects associated via the IdentityGroups 
association. The a argument specifies to return the first associated object for 
that association type. The URI of the associated object is returned in the 
groupuri variable.  */

   a=1;
	 grpassn=metadata_getnasn(uri,"IdentityGroups",a,groupuri);
	    
		/* If a person does not belong to any groups, set their group
	      variable to 'No groups' and output their name. */

	 if grpassn in (-3,-4) then do;
            group="No groups";
	    output;
	 end;

	    /* If the person belongs to many groups, loop through the list
	      and retrieve the Name and MetadataUpdated attributes of each group, 
			outputting each on a separate record. */

	 else do while (grpassn > 0);
		rc2=metadata_getattr(groupuri, "Name", group);
		rc=metadata_getattr(groupuri, "MetadataUpdated", MDUpdate);
		a+1;
		output;
        grpassn=metadata_getnasn(uri,"IdentityGroups",a,groupuri);
     end;
	   
	   /* Retrieve the next person's information */

     n+1;
     nobj=metadata_getnobj("omsobj:Person?@Id contains '.'",n,uri);
  end;

/* The KEEP statement specifies the variables to include in the output data set. */

  keep name dispname MDUpdate group;
run;

   /* Display the list of users and their groups */
proc report data=users_grps nowd headline headskip;
  columns name dispname group MDUpdate;
  define name / order 'User Name' format=$30.;
  define dispname / order 'Display Name' format=$30.;
  define group / order 'Group' format=$30.;
  define MDUpdate / display 'Updated' format=$20.;
  break after name / skip;
run;
The example creates output similar to the following:
PROC REPORT of Users_Grps Data Set
PROC REPORT of Users_Grps Data Set

Example: Listing Users and Their Logins

This program uses the SAS metadata DATA step functions to query the metadata repository, and return a list of all Person objects and their associated logins. The SAS Metadata Server supports external user accounts and internal user accounts. Both types of user accounts are modeled with the metadata type Person. An internal user account differs from an external user account in that a user specifies the user name with the suffix @saspw to log in (for example, sasadm@saspw). This value is known only to the metadata server. External accounts require domain-qualified user IDs to log in. An external account can have multiple logins defined for it. The logins are controlled with authentication domains. This program requests a listing of the users that are defined on the metadata server, and notes whether they are internal or external accounts. It lists the logins and authentication domains for each external account. The results are returned to an Identities data set in the Work library. The example includes code to print the listing with PROC PRINT, or to export it to a Microsoft Excel spreadsheet with PROC EXPORT.
/*Connect to the metadata server using the metadata system options as 
shown in the first example. */

data work.Identities;

/* The LENGTH statement defines the lengths of variables for function arguments. */
length IdentId IdentName DispName ExtLogin IntLogin DomainName $32 
uri uri2 uri3 uri4 $256;

/* The LABEL statement assigns descriptive labels to variables. */
label
	IdentId    = "Identity Id"
	IdentName  = "Identity Name"
	DispName   = "Display Name"
	ExtLogin   = "External Login"
	IntLogin   = "Is Account Internal?"
	DomainName = "Authentication Domain";

/* The CALL MISSING statement initializes the output variables to missing values. */
call missing(IdentId, IdentName, DispName, ExtLogin, IntLogin, DomainName, 
uri, uri2, uri3, uri4);
n=1;
n2=1;

/* The METADATA_GETNOBJ function specifies to get the Person objects in the repository. 
The n argument specifies to get the first person object that is returned. 
The uri argument will return the actual uri of the Person object. The program prints an 
informational message if no objects are found. */

rc=metadata_getnobj("omsobj:Person?@Id contains '.'",n,uri);
if rc<=0 then put "NOTE: rc=" rc
"There are no identities defined in this repository" 
" or there was an error reading the repository.";

/* The DO statement specifies a group of statements to be executed as a unit. 
The METADATA_GETATTR function gets the values of the Person object's Id, Name, 
and DisplayName attributes. */
do while(rc>0); 
	objrc=metadata_getattr(uri,"Id",IdentId);
	objrc=metadata_getattr(uri,"Name",IdentName); 
	objrc=metadata_getattr(uri,"DisplayName",DispName);

/* The METADATA_GETNASN function gets objects associated via the
InternalLoginInfo association. The InternalLoginInfo association returns
internal logins. The n2 argument specifies to return the first associated object
for that association name. The URI of the associated object is returned in
the uri2 variable. */

objrc=metadata_getnasn(uri,"InternalLoginInfo",n2,uri2);

/* If a Person does not have any internal logins, set their IntLogin
variable to 'No' Otherwise, set to 'Yes'. */
IntLogin="Yes";
DomainName="**None**";
if objrc<=0 then
do;
put "NOTE: There are no internal Logins defined for " IdentName +(-1)".";
IntLogin="No";
end;

/* The METADATA_GETNASN function gets objects associated via the Logins association. 
The Logins association returns external logins. The n2 argument specifies to return 
the first associated object for that association name. The URI of the associated 
object is returned in the uri3 variable. */

objrc=metadata_getnasn(uri,"Logins",n2,uri3);

/* If a Person does not have any logins, set their ExtLogin
variable to '**None**' and output their name. */
if objrc<=0 then
do;
put "NOTE: There are no external Logins defined for " IdentName +(-1)".";
ExtLogin="**None**";
output;
end;

/* If a Person has many logins, loop through the list and retrieve the name of 
each login. */
do while(objrc>0);
objrc=metadata_getattr(uri3,"UserID",ExtLogin);

/* If a Login is associated to an authentication domain, get the domain name. */
DomainName="**None**";
objrc2=metadata_getnasn(uri3,"Domain",1,uri4);
if objrc2 >0 then
do;
 objrc2=metadata_getattr(uri4,"Name",DomainName);
end;

/*Output the record. */
output;

n2+1;

/* Retrieve the next Login's information */
objrc=metadata_getnasn(uri,"Logins",n2,uri3);
end; /*do while objrc*/

/* Retrieve the next Person's information */
n+1;
n2=1;

rc=metadata_getnobj("omsobj:Person?@Id contains '.'",n,uri);
end; /*do while rc*/

/* The KEEP statement specifies the variables to include in the output data set. */
keep IdentId IdentName DispName ExtLogin IntLogin DomainName; 
run;

/* The PROC PRINT statement writes a basic listing of the data. */
proc print data=work.Identities label;
run;

/* The PROC EXPORT statement can be used to write the data to an Excel spreadsheet. */
/* Change DATA= to the data set name you specified above. */
/* Change OUTFILE= to an appropriate path for your system. */

proc export data=work.Identities 
	dbms=EXCEL2000 
	outfile="C:\temp\Identities.xls"
	replace;
run;
The example creates output similar to the following:
PROC PRINT of work.Identities Data Set
PROC PRINT of work.Identities data set