<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Philihp.com &#187; SAS</title>
	<atom:link href="http://www.philihp.com/blog/tag/sas/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.philihp.com/blog</link>
	<description>Yes, it's a palindrome.</description>
	<lastBuildDate>Tue, 31 Aug 2010 19:40:45 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Abusing Hash objects as a Stack in SAS Data Step</title>
		<link>http://www.philihp.com/blog/2010/abusing-hash-objects-as-a-stack-in-sas-data-step/</link>
		<comments>http://www.philihp.com/blog/2010/abusing-hash-objects-as-a-stack-in-sas-data-step/#comments</comments>
		<pubDate>Fri, 07 May 2010 21:00:40 +0000</pubDate>
		<dc:creator>philihp</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[data step]]></category>
		<category><![CDATA[hash]]></category>
		<category><![CDATA[Metadata]]></category>
		<category><![CDATA[recursion]]></category>
		<category><![CDATA[SAS]]></category>
		<category><![CDATA[stack]]></category>
		<category><![CDATA[traverse]]></category>

		<guid isPermaLink="false">http://www.philihp.com/blog/?p=566</guid>
		<description><![CDATA[Everyone in the computer science field (should) eventually learn or realize that any recursive function can be rewritten as an iterative process with the aid of a stack. Since a SAS Data Step is iterative, it&#8217;s fairly easy to look up children of a tree node in metadata, but nearly impossible to recursively look up [...]]]></description>
			<content:encoded><![CDATA[<p>Everyone in the computer science field (<em>should</em>) eventually learn or realize that any recursive function can be rewritten as an iterative process with the aid of a stack. Since a SAS Data Step is iterative, it&#8217;s fairly easy to look up children of a tree node in metadata, but nearly impossible to recursively look up children of children, and so fourth, without breaking out of the Data Step loop and into macro code, because there&#8217;s no native program stack.</p>
<p>You can use the <a href="http://support.sas.com/documentation/cdl/en/lrdict/62618/HTML/default/a000201972.htm">LINK</a> keyword, but that just keeps the <a href="http://en.wikipedia.org/wiki/Program_counter">PC register</a> in a stack to return to where you were a subroutine was called, it doesn&#8217;t recreate the data vector. Because of this, all variables in a SAS Data Step behave as global within the data step block.</p>
<p>You could use a _TEMPORARY_ array as a stack, but its size has to be hard-coded to a predetermined depth.</p>
<p>SAS 9 has a rarely used Hash object, which can be used within a Data Step. It grows as needed, so it can be used to function as a functionally infinite length array, and further abused to function as a stack within a Data Step. The following code demonstrates this:</p>
<pre>data _null_;
  declare hash stack();
  length stackvar $100;
  length stackdepth 8.;
  stackdepth=0;
  rc=stack.defineKey('stackdepth');
  rc=stack.defineData('stackvar');
  rc=stack.defineDone();

  stackvar='a';
  stackdepth+1;
  rc=stack.add();
  put 'push' stackdepth= stackvar=; 

  stackvar='b';
  stackdepth+1;
  rc=stack.add();
  put 'push' stackdepth= stackvar=; 

  stackvar='c';
  stackdepth+1;
  rc=stack.add();
  put 'push' stackdepth= stackvar=; 

  rc=stack.find();
  rc=stack.remove();
  put 'pop' stackdepth= stackvar=;
  stackdepth+-1;

  rc=stack.find();
  rc=stack.remove();
  put 'pop' stackdepth= stackvar=;
  stackdepth+-1;

  rc=stack.find();
  rc=stack.remove();
  put 'pop' stackdepth= stackvar=;
  stackdepth+-1;

  stackvar='x';
  stackdepth+1;
  rc=stack.add();
  put 'push' stackdepth= stackvar=; 

  stackvar='y';
  stackdepth+1;
  rc=stack.add();
  put 'push' stackdepth= stackvar=; 

  stackvar='z';
  stackdepth+1;
  rc=stack.add();
  put 'push' stackdepth= stackvar=; 

  rc=stack.find();
  rc=stack.remove();
  put 'pop' stackdepth= stackvar=;
  stackdepth+-1;

  rc=stack.find();
  rc=stack.remove();
  put 'pop' stackdepth= stackvar=;
  stackdepth+-1;

  rc=stack.find();
  rc=stack.remove();
  put 'pop' stackdepth= stackvar=;
  stackdepth+-1;
run;</pre>
<p>It outputs this:</p>
<pre>pushstackdepth=1 stackvar=a
pushstackdepth=2 stackvar=b
pushstackdepth=3 stackvar=c
popstackdepth=3 stackvar=c
popstackdepth=2 stackvar=b
popstackdepth=1 stackvar=a
pushstackdepth=1 stackvar=x
pushstackdepth=2 stackvar=y
pushstackdepth=3 stackvar=z
popstackdepth=3 stackvar=z
popstackdepth=2 stackvar=y
popstackdepth=1 stackvar=x</pre>
<p>By doing this, you can traverse a tree in metadata.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.philihp.com/blog/2010/abusing-hash-objects-as-a-stack-in-sas-data-step/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Looping through SAS metadata objects</title>
		<link>http://www.philihp.com/blog/2010/looping-through-sas-metadata-objects/</link>
		<comments>http://www.philihp.com/blog/2010/looping-through-sas-metadata-objects/#comments</comments>
		<pubDate>Wed, 05 May 2010 20:07:47 +0000</pubDate>
		<dc:creator>philihp</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[for]]></category>
		<category><![CDATA[loop]]></category>
		<category><![CDATA[Metadata]]></category>
		<category><![CDATA[SAS]]></category>

		<guid isPermaLink="false">http://www.philihp.com/blog/?p=550</guid>
		<description><![CDATA[The following is my attempt at SAS Golf, where in a Data Step, I try to list the associations of a metadata object to the log. This is basically just taking advantage of combining SAS for loops with SAS while loops. I thought it was cool. Usually I find myself doing this with metadata_getnasn, metadata_getnprp, [...]]]></description>
			<content:encoded><![CDATA[<p>The following is my attempt at SAS Golf, where in a Data Step, I try to list the associations of a metadata object to the log. This is basically just taking advantage of combining SAS for loops with SAS while loops. I thought it was cool. Usually I find myself doing this with <a href="http://support.sas.com/documentation/cdl/en/lrmeta/60739/HTML/default/getnasn.htm">metadata_getnasn</a>, <a href="http://support.sas.com/documentation/cdl/en/lrmeta/60739/HTML/default/getnprp.htm">metadata_getnprp</a>, and <a href="http://support.sas.com/documentation/cdl/en/lrmeta/60739/HTML/default/getnobj.htm">metadata_getnobj</a></p>
<ul>
<li>n &#8211; index for the nth association</li>
<li>u &#8211; uri to object</li>
<li>a &#8211; association</li>
</ul>
<pre>data;
  ...
  do n=1 by 1 while(n<r or r<=0);
    r=metadata_getnasl(u,n,a);
    put a;
  end;
  ...
run;</pre>
<p><strong>UPDATE:</strong> The <a href="http://support.sas.com/kb/19/977.html">metadata_getnass</a> function has been renamed to <a href="http://support.sas.com/documentation/cdl/en/lrmeta/60739/HTML/default/getnasn.htm">metadata_getnasn</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.philihp.com/blog/2010/looping-through-sas-metadata-objects/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Dramatically Increasing SAS DI Studio performance of SCD Type-2 Loader Transforms</title>
		<link>http://www.philihp.com/blog/2010/dramatically-increasing-sas-di-studio-performance-of-scd-type-2-loader-transforms/</link>
		<comments>http://www.philihp.com/blog/2010/dramatically-increasing-sas-di-studio-performance-of-scd-type-2-loader-transforms/#comments</comments>
		<pubDate>Tue, 19 Jan 2010 21:55:35 +0000</pubDate>
		<dc:creator>philihp</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[3.4]]></category>
		<category><![CDATA[9.1.3]]></category>
		<category><![CDATA[Data Integration Studio]]></category>
		<category><![CDATA[DI Studio]]></category>
		<category><![CDATA[Index]]></category>
		<category><![CDATA[Optimization]]></category>
		<category><![CDATA[SAS]]></category>
		<category><![CDATA[SCD Type-2 Loader]]></category>
		<category><![CDATA[Slowly Changing Dimension]]></category>

		<guid isPermaLink="false">http://www.philihp.com/blog/?p=535</guid>
		<description><![CDATA[In SAS DI Studio 3.4 (and I imagine in future versions), the prepackaged code for the SCD Type-2 Loader works like this: Does the dataset exist? If not, create an empty dataset with structure and indexes as defined from metadata. Then detect differences between it and the source dataset and the target dataset, expire any [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.sas.com/technologies/dw/entdiserver/index.html"><img src="http://www.philihp.com/blog/wp-content/uploads/2010/01/SCDLoader.png" alt="SCD Loader" title="SCD Loader" width="316" height="94" class="alignright size-full wp-image-544" /></a>In SAS DI Studio 3.4 (and I imagine in future versions), the prepackaged code for the SCD Type-2 Loader works like this: Does the dataset exist? If not, create an empty dataset with structure and indexes as defined from metadata. Then detect differences between it and the source dataset and the target dataset, expire any observations that are modified or deleted by setting their valid-to-date to now, and append any modified or new observations with a valid-from-date. The expire bit is done in-place with a data step modify statement, and the append is done with PROC APPEND. I assume this is done to reduce the amount of locking necessary on the dimension dataset. Because new observations are appended, the dataset never actually gets sorted by the business key, so this could lead to exponential growth over time on the expire bit; every time the transform wants to change a single observation&#8217;s valid-to-date, it scans the entire table. And it doesn&#8217;t help that compression is off by default.</p>
<p>In the instance below, we had an uncompressed dataset with a modest 220,000 rows. The dataset had a variable &#8220;description&#8221; defined as a 4000-length string which was usually but not always null. Most steps of the SCD Loader run in a few seconds, but the following is usually the bottleneck. On a run one night to populate a new variable, this data step ran in a little over 9 hours:</p>
<pre>     data PRESTAGE.LOAD_SW_ENTITY_RELEASE_X_HOST;
        modify PRESTAGE.LOAD_SW_ENTITY_RELEASE_X_HOST
           work.etls_close
            (rename = (ETLS_KEY = SW_ENTITY_REL_X_HOST_ID
           ETLS_FROMDATE = VALID_FROM_DTTM))
            updatemode=nomissingcheck;
        by SW_ENTITY_REL_X_HOST_ID VALID_FROM_DTTM;
        VALID_TO_DTTM = ETLS_CLSDATE;
        if %sysrc(_SOK) eq _iorc_ then
           replace;
        _iorc_ = 0;
        _error_ = 0;
     run;</pre>
<pre>NOTE: There were 1 observations read from the data set PRESTAGE.LOAD_SW_ENTITY_RELEASE_X_HOST.
NOTE: The data set PRESTAGE.LOAD_SW_ENTITY_RELEASE_X_HOST has been updated.  There were 45924 observations rewritten, 0
     observations added and 0 observations deleted.
NOTE: There were 45924 observations read from the data set WORK.ETLS_CLOSE.
NOTE: DATA statement used (Total process time):
     real time           9:06:00.46
     cpu time            9:05:58.43</pre>
<p>Turning compression on reduced the size of this dataset from 1.1 gigs to 4.5 megs, mostly from compressing the 4000-char string that was usually empty; with compression off, an X-length string always takes up X bytes because it&#8217;s faster to seek to a certain observation if all observations are the same size. Additionally, in metadata an index was defined on the two variables used above in the BY statement. I ran this PROC DATASETS statement to create the index by hand (the Loader would create them if the table didn&#8217;t exist, but it assumes indexes exist if the table exists).</p>
<pre>proc datasets library=prestage nolist;
 modify load_sw_entity_release_x_host;
 index create ndx=(sw_entity_rel_x_host_id valid_from_dttm) / unique;
quit;</pre>
<p>With this metadata in place, the SCD Loader uses the index in the datastep and generates the following datastep instead:</p>
<pre>     data PRESTAGE.LOAD_SW_ENTITY_RELEASE_X_HOST;
        set work.etls_close(rename = (ETLS_KEY = SW_ENTITY_REL_X_HOST_ID
                                      ETLS_FROMDATE = VALID_FROM_DTTM));
        <b>modify PRESTAGE.LOAD_SW_ENTITY_RELEASE_X_HOST
            key=ndx / unique;</b>
        VALID_TO_DTTM = ETLS_CLSDATE;
        if %sysrc(_SOK) eq _iorc_ then
           replace;
        _iorc_ = 0;
        _error_ = 0;
     run;</pre>
<p>By using the index, the runtime was reduced to under two seconds.</p>
<pre>NOTE: There were 45924 observations read from the data set WORK.ETLS_CLOSE.
NOTE: The data set PRESTAGE.LOAD_SW_ENTITY_RELEASE_X_HOST has been updated.  There were 45924 observations rewritten, 0
     observations added and 0 observations deleted.
NOTE: DATA statement used (Total process time):
     real time           1.41 seconds
     cpu time            1.21 seconds</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.philihp.com/blog/2010/dramatically-increasing-sas-di-studio-performance-of-scd-type-2-loader-transforms/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Easy Inserting/Appending Libraries into FMTSEARCH path lists in SAS 9.2</title>
		<link>http://www.philihp.com/blog/2009/easy-insertingappending-libraries-into-fmtsearch-path-lists-in-sas-9-2/</link>
		<comments>http://www.philihp.com/blog/2009/easy-insertingappending-libraries-into-fmtsearch-path-lists-in-sas-9-2/#comments</comments>
		<pubDate>Tue, 27 Oct 2009 21:49:50 +0000</pubDate>
		<dc:creator>philihp</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[FMTSEARCH]]></category>
		<category><![CDATA[Formats]]></category>
		<category><![CDATA[SAS]]></category>
		<category><![CDATA[SAS 9.2]]></category>
		<category><![CDATA[SASAUTOS]]></category>

		<guid isPermaLink="false">http://www.philihp.com/blog/?p=507</guid>
		<description><![CDATA[In SAS 9.1.3 and prior, Options list (such as FMTSEARCH for libraries with format catalogs, and SASAUTOS for paths that contain macros shared across jobs) were annoying to work with. If you have nested code that wants to add a library or a path the list, doing so like this could potentially clobber statements executed [...]]]></description>
			<content:encoded><![CDATA[<p>In SAS 9.1.3 and prior, Options list (such as FMTSEARCH for libraries with format catalogs, and SASAUTOS for paths that contain macros shared across jobs) were annoying to work with. If you have nested code that wants to add a library or a path the list, doing so like this could potentially clobber statements executed outside of the nested block:</p>
<pre>OPTIONS FMTSEARCH=(<i>mylib.formats</i>);</pre>
<p>To really be safe, you want to add your library to the list. Doing this wasn&#8217;t very intuitive. I had always done this like this:</p>
<pre>%let FMTSEARCH = %substr(%sysfunc(getoption(fmtsearch) ) ,2);
OPTIONS FMTSEARCH = (<i>mylib.formats</i> &FMTSEARCH;</pre>
<p>But in 9.2, there are two new System Options, <a href="http://support.sas.com/documentation/cdl/en/lrdict/62618/HTML/default/a003273966.htm">INSERT=</a> and <a href="http://support.sas.com/documentation/cdl/en/lrdict/62618/HTML/default/a003273971.htm">APPEND=</a>. This is now as simple as</p>
<pre>OPTIONS INSERT=(FMTSEARCH=<i>mylib.formats</i>);</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.philihp.com/blog/2009/easy-insertingappending-libraries-into-fmtsearch-path-lists-in-sas-9-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Counting distinct variables in SQL with SAS</title>
		<link>http://www.philihp.com/blog/2009/counting-distinct-variables-in-sql/</link>
		<comments>http://www.philihp.com/blog/2009/counting-distinct-variables-in-sql/#comments</comments>
		<pubDate>Mon, 26 Oct 2009 06:44:45 +0000</pubDate>
		<dc:creator>philihp</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Count]]></category>
		<category><![CDATA[Distinct]]></category>
		<category><![CDATA[SAS]]></category>
		<category><![CDATA[SQL]]></category>

		<guid isPermaLink="false">http://www.philihp.com/blog/?p=504</guid>
		<description><![CDATA[One way to get the count of distinct variables, which works in most flavors of SQL, is to use a subquery. For instance, in Oracle this is:
SELECT count(SELECT DISTINCT foo FROM table) FROM dual
In SAS, using PROC SQL, you can do that too, but you can also simply do this:
SELECT count(distinct foo) FROM table
]]></description>
			<content:encoded><![CDATA[<p>One way to get the count of distinct variables, which works in most flavors of SQL, is to use a subquery. For instance, in Oracle this is:</p>
<pre>SELECT count(SELECT DISTINCT foo FROM table) FROM dual</pre>
<p>In SAS, using PROC SQL, you can do that too, but you can also simply do this:</p>
<pre>SELECT count(distinct foo) FROM table</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.philihp.com/blog/2009/counting-distinct-variables-in-sql/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>For loops in SAS</title>
		<link>http://www.philihp.com/blog/2009/for-loops-in-sas/</link>
		<comments>http://www.philihp.com/blog/2009/for-loops-in-sas/#comments</comments>
		<pubDate>Wed, 07 Oct 2009 16:06:22 +0000</pubDate>
		<dc:creator>philihp</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[For loops]]></category>
		<category><![CDATA[List iteration]]></category>
		<category><![CDATA[Metadata]]></category>
		<category><![CDATA[SAS]]></category>

		<guid isPermaLink="false">http://www.philihp.com/blog/?p=490</guid>
		<description><![CDATA[When coding for loops in SAS, one neat thing to remember is that all of the parts of it are optional.
start &#60;TO stop&#62; &#60;BY increment&#62; &#60;WHILE(expression) &#124; UNTIL(expression)&#62;
http://support.sas.com/documentation/cdl/en/lrdict/62618/HTML/default/a000201276.htm
So in another language, what might be
for(int i=0;i < max;i++) {
In SAS, you can leave out the "by 1", since it's the default increment. So this would be
do [...]]]></description>
			<content:encoded><![CDATA[<p>When coding for loops in SAS, one neat thing to remember is that all of the parts of it are optional.</p>
<blockquote><p>start &lt;TO stop&gt; &lt;BY increment&gt; &lt;WHILE(expression) | UNTIL(expression)&gt;</p>
<p><a href="http://support.sas.com/documentation/cdl/en/lrdict/62618/HTML/default/a000201276.htm"><cite>http://support.sas.com/documentation/cdl/en/lrdict/62618/HTML/default/a000201276.htm</cite></a></p></blockquote>
<p>So in another language, what might be</p>
<pre>for(int i=0;i < max;i++) {</pre>
<p>In SAS, you can leave out the "by 1", since it's the default increment. So this would be</p>
<pre>do i=0 to max;</pre>
<p>What's fun is in SAS you can combine a <a href="http://en.wikipedia.org/wiki/Do_while_loop">Repeat-Until/Do-While</a> loop with a for loop. This might be useful if for some reason you don't set the var "max" until you've iterated through the loop once, such as in this case, where "<a href="http://support.sas.com/documentation/cdl/en/lrmeta/60739/HTML/default/getnobj.htm">metadata_getnobj</a>" returns a negative on fail, but otherwise returns the number of objects that match the query.</p>
<pre>do i=1 by 1 until (i >= objs);
  rc=metadata_getnobj("omsobj:SASLibrary?@Libref='MYLIBREF'",i,uri);
  if rc>=0 then objs=rc;

  rc=metadata_getnasn(uri,"LibraryConnection",1,uri);
  if rc>=0 then leave;
end;</pre>
<p>This code loops through all the libraries in the metadata with libref='MYLIBREF', and exits with a URI for the first one it sees with at least one LibraryConnection association. This is useful when you have a remote library pointing to a local library and they both have the same name and you want a URI to the remote one.</p>
<p>Another cool thing with FOR loops in SAS is iteration through lists. In Java, say you have a block like this:</p>
<pre>for(String x : new String[] { "a","b","c" }) {
  System.out.println("x="+x);
}</pre>
<p>In SAS, this is simply</p>
<pre>do x="a","b","c";
  put x=;
end;</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.philihp.com/blog/2009/for-loops-in-sas/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Removing formats from all variables in a SAS Dataset</title>
		<link>http://www.philihp.com/blog/2009/removing-formats-from-all-variables-in-a-sas-dataset/</link>
		<comments>http://www.philihp.com/blog/2009/removing-formats-from-all-variables-in-a-sas-dataset/#comments</comments>
		<pubDate>Wed, 12 Aug 2009 20:42:48 +0000</pubDate>
		<dc:creator>philihp</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Formats]]></category>
		<category><![CDATA[SAS]]></category>

		<guid isPermaLink="false">http://www.philihp.com/blog/?p=476</guid>
		<description><![CDATA[In SAS, every field/variable in a table/dataset can be given a format. This format tells SAS how to display the data. The following datastep will create a table called &#8220;formatted&#8221; having 1 row containing 3 variables: x, y, and d.
data formatted;
  x=9000;
  y=42;
  now=16761;
  format x comma6.;
  format y dollar5.;
 [...]]]></description>
			<content:encoded><![CDATA[<p>In SAS, every field/variable in a table/dataset can be given a format. This format tells SAS how to display the data. The following datastep will create a table called &#8220;formatted&#8221; having 1 row containing 3 variables: x, y, and d.</p>
<pre>data formatted;
  x=9000;
  y=42;
  now=16761;
  format x comma6.;
  format y dollar5.;
  format now date7.;
  put x=;
  put y=;
  put now=;
run;</pre>
<p>Additionally it prints to the log the formatted values.</p>
<pre>x=9,000
y=$42
now=21NOV05</pre>
<p>In SAS you can also create your own formats, and you can assign these formats to whichever datasets you like. However if that format goes away for some reason and you try to look at the data again, you will get this error:</p>
<pre>ERROR: Informat $YOURFMT not found or couldn't be loaded for variable YOURVAR.</pre>
<p>You can get around this by turning off this option</p>
<pre>options nofmterr;</pre>
<p>Then your ERROR turns into NOTE, and things work normally, except when you try to view the data you see it unformatted. This could be useful, however, if you find yourself in a situation like this:</p>
<pre>data _null_;
  a=20.0000001;
  b=20.000001;
  format a dollar4.2;
  format b dollar4.2;
  put a= b=;
  if(a=b) then put 'matches';
   else put 'no match';
run;</pre>
<p>Runs and outputs:</p>
<pre>a=20.0 b=20.0
no match</pre>
<p>At any rate, there are times in SAS when you simply want to remove all the formats from a dataset. This can be done in one line in a datastep:</p>
<pre>
data unformatted;
  set formatted;
  format _all_;
run;
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.philihp.com/blog/2009/removing-formats-from-all-variables-in-a-sas-dataset/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Converting between Date and Datetime in SAS</title>
		<link>http://www.philihp.com/blog/2009/converting-a-date-to-a-datetime-in-sas/</link>
		<comments>http://www.philihp.com/blog/2009/converting-a-date-to-a-datetime-in-sas/#comments</comments>
		<pubDate>Mon, 01 Jun 2009 21:01:10 +0000</pubDate>
		<dc:creator>philihp</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Dates]]></category>
		<category><![CDATA[SAS]]></category>

		<guid isPermaLink="false">http://www.philihp.com/blog/?p=430</guid>
		<description><![CDATA[In SAS, fields are either character of varying length, or numeric. No exceptions. Temporal values such as Date and Date/Time are stored as either the number of days or seconds since 1960 January 1st.
In order to convert from Date/Time to Date, and from Date to Date/Time, you could divide or multiply respectively by 86400 (the [...]]]></description>
			<content:encoded><![CDATA[<p>In SAS, fields are either character of varying length, or numeric. No exceptions. Temporal values such as Date and Date/Time are stored as either the number of days or seconds since 1960 January 1st.</p>
<p>In order to convert from Date/Time to Date, and from Date to Date/Time, you could divide or multiply respectively by 86400 (the number of seconds in a day).</p>
<pre>data _null_;
  d='29FEB1984'd;
  put d date.;
  <strong>dt=d*24*60*60;</strong>
  put dt datetime.;
  <strong>d=dt/24/60/60;</strong>
  put d date.;
run;</pre>
<p>But unless someone deals with dates every day, it might not be obvious to them by reading this code what those <a href="http://en.wikipedia.org/wiki/Magic_number_(programming)">magic numbers</a> are doing.</p>
<p>Another method would be to use <a href="http://support.sas.com/onlinedoc/913/getDoc/en/lrdict.hlp/a000199354.htm">put</a>() and <a href="http://support.sas.com/onlinedoc/913/getDoc/en/lrdict.hlp/a000180357.htm">input</a>() using functions to go back and fourth.</p>
<pre>data _null_;
  d='29FEB1984'd;
  put d date.;
  <strong>dt=input(put( d  ,date7.) || ':00:00:00', datetime.);</strong>
  put dt datetime.;
  <strong>d =input(substr(put(dt,datetime.),1,7),date7.);</strong>
  put d date.;
run;</pre>
<p>But I don&#8217;t consider this any better. It&#8217;s wordy. Functions are nested within functions. And you still have a magic number for the call to substr. Also, since it isn&#8217;t uncommon for SAS datasets to get into the billions of rows, this probably isn&#8217;t the fastest way to do it.</p>
<p>SAS has built-in functions called <a href="http://support.sas.com/onlinedoc/913/getDoc/en/lrdict.hlp/a000179419.htm">dhms</a>() (mnemonic: date,hour,minute,second) and <a href="http://support.sas.com/onlinedoc/913/getDoc/en/lrdict.hlp/a000245883.htm">datepart</a>() that should make things a lot easier.</p>
<pre>data _null_;
  d='29FEB1984'd;
  put d date.;
  <strong>dt=dhms(d,0,0,0);</strong>
  put dt datetime.;
  <strong>d =datepart(dt);</strong>
  put d date.;
run;</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.philihp.com/blog/2009/converting-a-date-to-a-datetime-in-sas/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Installing SAS 9.1.3 on Ubuntu Linux 8.10</title>
		<link>http://www.philihp.com/blog/2009/installing-sas-913-on-ubuntu-linux-810/</link>
		<comments>http://www.philihp.com/blog/2009/installing-sas-913-on-ubuntu-linux-810/#comments</comments>
		<pubDate>Fri, 23 Jan 2009 00:42:43 +0000</pubDate>
		<dc:creator>philihp</dc:creator>
				<category><![CDATA[How To]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[SAS]]></category>
		<category><![CDATA[Ubuntu]]></category>

		<guid isPermaLink="false">http://www.philihp.com/blog/2009/01/installing-sas-913-on-ubuntu-linux-810/</guid>
		<description><![CDATA[It works. Just follow the instructions.
]]></description>
			<content:encoded><![CDATA[<p>It works. Just follow the instructions.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.philihp.com/blog/2009/installing-sas-913-on-ubuntu-linux-810/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Installing SAS 9.1.3 on OpenBSD 4.3</title>
		<link>http://www.philihp.com/blog/2009/installing-sas-913-on-openbsd-43/</link>
		<comments>http://www.philihp.com/blog/2009/installing-sas-913-on-openbsd-43/#comments</comments>
		<pubDate>Fri, 23 Jan 2009 00:42:00 +0000</pubDate>
		<dc:creator>philihp</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[OpenBSD]]></category>
		<category><![CDATA[SAS]]></category>

		<guid isPermaLink="false">http://www.philihp.com/blog/2009/01/installing-sas-913-on-openbsd-43/</guid>
		<description><![CDATA[It does not work.
]]></description>
			<content:encoded><![CDATA[<p>It does not work.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.philihp.com/blog/2009/installing-sas-913-on-openbsd-43/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
