<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-1116546449646002174</id><updated>2011-10-28T12:46:29.632-04:00</updated><title type='text'>Jurassic SAS® in the BI/EBI World</title><subtitle type='html'>Tips/techniques and advice on integrating traditional SAS Tools with the SAS Business Intelligence Suite</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://hcsbi.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1116546449646002174/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://hcsbi.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>DonH</name><uri>http://www.blogger.com/profile/12347027394201327945</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>16</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-1116546449646002174.post-3613684581899907578</id><published>2011-09-12T12:01:00.017-04:00</published><updated>2011-09-14T13:49:04.061-04:00</updated><title type='text'>PROC STREAM: Extending the Macro Language to create more than just SAS code</title><content type='html'>The &lt;strong&gt;STREAM&lt;/strong&gt; procedure is a new experimental procedure available in SAS 9.3. It processes a&amp;nbsp;SAS generated input stream, including macro specifications and logic and directs the generated text to any fileref. It&amp;nbsp;provides direct support for &lt;a href="http://www.sascommunity.org/wiki/SAS_Server_Pages"&gt;SAS Server Pages&lt;/a&gt;, as initially described in my SAS Press book, &lt;a href="http://www.sascommunity.org/wiki/Building_Web_Applications_with_SAS/IntrNet:_A_Guide_to_the_Application_Dispatcher"&gt;Building Web Applications with SAS/IntrNet®: A Guide to the Application Dispatcher&lt;/a&gt;.&amp;nbsp;&amp;nbsp;&lt;strong&gt;PROC STREAM&lt;/strong&gt; significantly expands those capabilities in a number of ways:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;There is no 32K limit on the text produced by a single input line in a SAS Server Page.&lt;/li&gt;&lt;li&gt;A much broader range of SAS statements can now be used, including macro definitions and %include statements.&lt;/li&gt;&lt;li&gt;SAS code can be embedded and executed from within a SAS Server Page.&lt;/li&gt;&lt;li&gt;The &lt;strong&gt;%sysfunc&lt;/strong&gt; macro can invoke the &lt;strong&gt;SCL&lt;/strong&gt; functions that access data.&lt;/li&gt;&lt;li&gt;and more&lt;/li&gt;&lt;/ol&gt;PROC STREAM can be used with the macro language&amp;nbsp;in order to produce virtually any data driven text file, including but not limited to:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;HTML Reports and UIs.&lt;/li&gt;&lt;li&gt;Word documents (as RTF files).&lt;/li&gt;&lt;li&gt;XML documents&lt;/li&gt;&lt;li&gt;XAML/Silverlight documents&lt;/li&gt;&lt;/ol&gt;The bottom line is that this facility can&amp;nbsp;be used for more than&amp;nbsp;just creating SAS Server Pages. It&amp;nbsp;will be the subject of an upcoming e-Book, &lt;a href="http://www.sascommunity.org/wiki/SAS_Server_Pages_and_More:_A_Framework_for_Generating_Dynamic_Content"&gt;SAS Server Pages and More: A Framework for Generating Dynamic Content&lt;/a&gt;, that will be published by SAS Press. Please use the &lt;a href="http://www.sascommunity.org/wiki/Talk:SAS_Server_Pages_and_More:_A_Framework_for_Generating_Dynamic_Content"&gt;discussion tab&lt;/a&gt; on sasCommunity.org to ask any questions or suggest topics/examples for the e-Book.&lt;br /&gt;&lt;br /&gt;If you have SAS 9.3, please feel free to try out the following example that just scratches the surface&amp;nbsp;of what this powerful new procedure can do.&lt;br /&gt;&lt;br /&gt;filename sspout '\PROC_STREAM_says_hello.html';&lt;br /&gt;proc stream outfile=sspout sqac dqac;&lt;br /&gt;BEGIN&lt;br /&gt;%macro checkTOD;&lt;br /&gt;%local timeOfDay;&lt;br /&gt;%let timeOfDay = %sysfunc(time());&lt;br /&gt;%if &amp;amp;timeOfDay le 43200 %then Morning;&lt;br /&gt;%else %if &amp;amp;timeOfDay le 64800 %then Afternoon;&lt;br /&gt;%else %if &amp;amp;timeOfDay le 72000 %then Evening;&lt;br /&gt;%else Night;&lt;br /&gt;%mend checkTOD;&lt;br /&gt;&amp;lt;h1&amp;gt;Good %checkTOD &amp;amp;sysuserid..&amp;lt;/h1&amp;gt;&lt;br /&gt;&amp;lt;h2&amp;gt; This welcome note generated&lt;br /&gt;&amp;nbsp;at %sysfunc(time(),timeampm8.)&lt;br /&gt;&amp;nbsp;on %sysfunc(date(),worddate.).&amp;lt;/h2&amp;gt;&lt;br /&gt;This HTML file was produced and customized&lt;br /&gt;&amp;nbsp;courtesy of PROC STREAM using&lt;br /&gt;&amp;nbsp;SAS Release &amp;amp;sysver on &amp;amp;sysscp..;&lt;br /&gt;;;;;&lt;br /&gt;dm "wbrowse '\PROC_STREAM_says_hello.html'";&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1116546449646002174-3613684581899907578?l=hcsbi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hcsbi.blogspot.com/feeds/3613684581899907578/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hcsbi.blogspot.com/2011/09/proc-stream-extending-macro-language-to.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1116546449646002174/posts/default/3613684581899907578'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1116546449646002174/posts/default/3613684581899907578'/><link rel='alternate' type='text/html' href='http://hcsbi.blogspot.com/2011/09/proc-stream-extending-macro-language-to.html' title='PROC STREAM: Extending the Macro Language to create more than just SAS code'/><author><name>DonH</name><uri>http://www.blogger.com/profile/12347027394201327945</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1116546449646002174.post-3225671677857522554</id><published>2011-08-16T08:00:00.006-04:00</published><updated>2011-08-16T09:11:46.548-04:00</updated><title type='text'>Using formulas in Excel and actual values in HTML and PDF output</title><content type='html'>On a recent project I had to use the technique I described in an &lt;a href="http://hcsbi.blogspot.com/2010/03/html-pdf-rtf-and-more-all-at-same-time.html"&gt;earlier post&lt;/a&gt; to create HTML, PDF, and Excel (using the ExcelXP tagset) output all at the same time. Adding ExcelXP output was straightforward. But, of course, there was a wrinkle. The Excel version had to use formulas so the user could do some what-if analysis. So I faced what I thought was a serious problem - how to put the actual values in the PDF and HTML versions, but with formulas in Excel.&lt;br /&gt;&lt;br /&gt;But luckily the ExcelXP wizards at SAS (and by that I mean Eric Gebhart and Vince DelGobbo)&amp;nbsp;provided a way to&amp;nbsp;do just that! I am not sure if this use-case was part of their design/approach. But it worked. They use the tagattr attribute to do all sorts of cool things - like providing a formula. Here is a simple example that generates (all at once) HTML, PDF and Excel versions of the report with the&amp;nbsp;static values&amp;nbsp;in&amp;nbsp;PDF and HTML, but formulas in Excel.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: x-small;"&gt;options nodate nonumber;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: x-small;"&gt;ods listing close;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: x-small;"&gt;ods html file='\Formulas.html';&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: x-small;"&gt;ods pdf file='\Formulas.pdf' notoc;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: x-small;"&gt;ods tagsets.ExcelXp file='\Formulas.xml';&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: x-small;"&gt;proc report data = sashelp.class nowd;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: x-small;"&gt;&amp;nbsp;title 'HTML, PDF, and ExcelXP, with Formulas in Excel';&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: x-small;"&gt;&amp;nbsp;columns name age sex height weight bmi;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: x-small;"&gt;&amp;nbsp;define bmi / computed format=5.2&amp;nbsp;style=&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp; [tagattr='(formula:RC[-1]*703)/(RC[-2]^2) format:0.00'];&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: x-small;"&gt;&amp;nbsp;compute bmi;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: x-small;"&gt;&amp;nbsp; &amp;nbsp;bmi = (weight.sum*703)/(height.sum**2);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: x-small;"&gt;&amp;nbsp;endcomp;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: x-small;"&gt;run;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: Courier New; font-size: x-small;"&gt;ods _all_ close;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;A few points about this:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Note that the formula uses the Excel &lt;a href="http://office.microsoft.com/en-us/help/about-cell-and-range-references-HP005198323.aspx"&gt;R1C1&lt;/a&gt; notation (and it works regardless of whether this reference style is enabled in Excel).&lt;/li&gt;&lt;ol&gt;&lt;li&gt;RC[-1] uses the&amp;nbsp;value from the same row,&amp;nbsp;one column to the left.&lt;/li&gt;&lt;li&gt;RC[-2]&amp;nbsp;uses the value from the same row, two columns to the left.&lt;/li&gt;&lt;li&gt;So the formula&amp;nbsp;uses the current row's prior column value (i.e., one column to the left), multiplies it by 703 and then divides that by the square of the value in current row two columns to the left.&lt;/li&gt;&lt;/ol&gt;&lt;li&gt;Since Excel chooses to do its own thing with formats, the &lt;strong&gt;format&lt;/strong&gt; attribute is used to tell Excel how to format the value.&lt;/li&gt;&lt;li&gt;Note the use of ^&amp;nbsp; instead of ** for the exponentiation operator.&lt;/li&gt;&lt;/ol&gt;Since the program uses the SASHELP.CLASS data, which luckily has the needed components to calculate Body Mass Index (aka BMI), you can run this yourself.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1116546449646002174-3225671677857522554?l=hcsbi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hcsbi.blogspot.com/feeds/3225671677857522554/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hcsbi.blogspot.com/2011/08/using-formulas-in-excel-and-actual.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1116546449646002174/posts/default/3225671677857522554'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1116546449646002174/posts/default/3225671677857522554'/><link rel='alternate' type='text/html' href='http://hcsbi.blogspot.com/2011/08/using-formulas-in-excel-and-actual.html' title='Using formulas in Excel and actual values in HTML and PDF output'/><author><name>DonH</name><uri>http://www.blogger.com/profile/12347027394201327945</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1116546449646002174.post-548888601991281410</id><published>2011-05-16T09:00:00.005-04:00</published><updated>2011-05-16T13:10:39.916-04:00</updated><title type='text'>PUT Statements in a Workspace Server to generate HTML? Yes you can!</title><content type='html'>Well, for years I've operated under the assumption that because a SAS Workspace Server (WS) does not support streaming HTML, that a stored process run by a WS could not use DATA steps to write HTML to the user's browser. Because the _webout fileref is not defined, you have to use &lt;a href="http://support.sas.com/documentation/cdl/en/publishdg/61497/HTML/default/a003256454.htm"&gt;packages&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Why does this matter? Why not just use a SAS Stored Process Server? Well, there are a multitude of reasons for this, but here are a few &lt;strong&gt;use cases&lt;/strong&gt; that in prior projects have led me to want to use a WS:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;A WS is started for each client request and thus it is running using the user's credentials. That can be important if you want to control file system access.&lt;/li&gt;&lt;li&gt;If a stored process needs to run for a long time (and that is a relative term in the Web world), using a WS does not cause the up and running Stored Process Servers to be tied up with such requests. Using a Stored Process Server for such requests limits access to them for all other user requests (this, of course, also depends on how your pool is set up).&lt;/li&gt;&lt;/ul&gt;You can read &lt;a href="http://support.sas.com/documentation/cdl/en/biasag/61237/HTML/default/viewer.htm#wsspovw.htm"&gt;more about the differences&lt;/a&gt; at support.sas.com. &lt;br /&gt;&lt;br /&gt;Because&amp;nbsp;WS use packages, the question then becomes how do you set up the environment so that you have a fileref to write your HTML (as well as other content types) to&amp;nbsp;so that it&amp;nbsp;will be directly displayed by the browser. With some help from Vince @ SAS, it turns out that with a few tweaks in how you call the &lt;strong&gt;stpBegin&lt;/strong&gt; and &lt;strong&gt;stpEnd&lt;/strong&gt; macros, you can do that. The trick is to set things up so that &lt;strong&gt;stpBegin&lt;/strong&gt; and &lt;strong&gt;stpEnd&lt;/strong&gt; create a &lt;em&gt;package&lt;/em&gt;, but do not issue an ODS statement.&lt;br /&gt;&lt;br /&gt;Here are&amp;nbsp;a few simple&amp;nbsp;(in hindsight) things you need to do:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Before calling the &lt;strong&gt;stpBegin&lt;/strong&gt; macro, make sure it does not create any ODS statements by setting the value of the&amp;nbsp;_&lt;strong&gt;odsDest&lt;/strong&gt; macro variable to NONE. You can do this by making it a parameter to the stored process, or by using a %let statement.&lt;/li&gt;&lt;li&gt;Then after the &lt;strong&gt;stpBegin&lt;/strong&gt; macro call, simply set the magic macro variable _NAMEVALUE to set the _DEFAULT_ENTRY attribute which specifies the name of the entry in the package that will be displayed to the user. For example:&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;%let outfile=streaming.html;&lt;br /&gt;&amp;nbsp;&amp;nbsp;%let _NAMEVALUE=_DEFAULT_ENTRY=&amp;amp;outfile;&lt;/span&gt;&lt;br /&gt;Note that the macro variable outfile is used&amp;nbsp;because this value will be used in multiple locations in your stored process.&lt;/li&gt;&lt;li&gt;Then issue a filename statement to create a fileref that you can use in your DATA Step code:&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;filename _webout "&amp;amp;_stpwork&amp;amp;outfile";&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;That's all there is to it. Your package can contain any number of files, but the _DEFAULT_ENTRY attribute specifies which one will be displayed by the package viewer.&lt;br /&gt;&lt;br /&gt;Here is a complete example that is a simplified version of &lt;a href="http://www.sascommunity.org/wiki/SAS_Server_Pages"&gt;SAS Server Pages&lt;/a&gt; that you can try out for yourself. Note that&amp;nbsp;because the name/location of the output file had to be referenced in two places, the code uses a macro variable to ensure the values are consistent.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;*ProcessBody;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;%let outfile=streaming.html;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;%let _odsDest=NONE;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;%stpBegin()&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;%let _namevalue=_DEFAULT_ENTRY=&amp;amp;outfile;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;filename _webout "&amp;amp;_stpwork&amp;amp;outfile";&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;data _null_;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;infile datalines;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;file _webout;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;input;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;_infile_ = resolve(_infile_);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;put _infile_;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;datalines;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;&amp;lt;h1&amp;gt;Testing a WS running as &lt;/span&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;Process ID &amp;amp;sysjobid&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;for User &amp;amp;sysuserid&amp;lt;/h1&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;This test was run at&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;%sysfunc(time(),timeampm.)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;on&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;%sysfunc(date(),worddate.)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;run;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;%stpEnd()&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1116546449646002174-548888601991281410?l=hcsbi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hcsbi.blogspot.com/feeds/548888601991281410/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hcsbi.blogspot.com/2011/05/put-statements-in-workspace-server-to.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1116546449646002174/posts/default/548888601991281410'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1116546449646002174/posts/default/548888601991281410'/><link rel='alternate' type='text/html' href='http://hcsbi.blogspot.com/2011/05/put-statements-in-workspace-server-to.html' title='PUT Statements in a Workspace Server to generate HTML? Yes you can!'/><author><name>DonH</name><uri>http://www.blogger.com/profile/12347027394201327945</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1116546449646002174.post-5119064291858322716</id><published>2011-04-11T08:00:00.002-04:00</published><updated>2011-04-11T08:00:20.101-04:00</updated><title type='text'>This Page Intentionally Left Blank</title><content type='html'>Sometimes printed documents will have a message like &lt;em&gt;This Page Intentionally Left Blank&lt;/em&gt;. But, of course, since those pages have that message, they really aren't blank. &lt;br /&gt;&lt;br /&gt;How can we adapt this to our reporting programs and processes?&lt;br /&gt;&lt;ul&gt;&lt;li&gt;In a BI environment if, for example, a Stored Process report has no data, then the user will either see a blank page or an error message that no output was generated.&lt;/li&gt;&lt;li&gt;In a batch reporting environment where reports are emailed as attachments, if there is no data, the file may not be created and so the code to email the report will likely fail.&lt;/li&gt;&lt;/ul&gt;Neither of these is particularly user-friendly or desirable. So, how do we handle this? &lt;em&gt;This Page Intentionally Left Blank&lt;/em&gt; suggests a solution. Simply produce some output. The macro &lt;strong&gt;noDataFoundMessage&lt;/strong&gt; does just that for you. If the input data set is empty, it produces a page of output with an appropriate message. If the output data set is not empty, it generates no output. When used with your reporting code you are thus guaranteed that some output is produced&lt;br /&gt;&lt;ul&gt;&lt;li&gt;if you have data, your report is output, but the &lt;strong&gt;noDataFoundMessage&lt;/strong&gt; macro produces no output&lt;/li&gt;&lt;li&gt;if there is no data, your report code produces no output, but the &lt;strong&gt;noDataFoundMessage&lt;/strong&gt; does&lt;/li&gt;&lt;/ul&gt;So the trick is call both your reporting code and the noDataFoundMessage macro. One of the two, but not both, will produce output.&lt;br /&gt;&lt;br /&gt;The macro source follows. Feel free to use and share it. I just ask that it's source is acknowledged.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;%macro noDataFoundMessage&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(Data=_last_,&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;Message=No Data Found&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;/*----------------------------------------------&lt;/span&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;Copyright (c) 2008 Henderson Consulting Services&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;PROGRAMMER : Don Henderson&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;PURPOSE : Generates a message if the specified&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;input data set&lt;/span&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;&amp;nbsp;is empty.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;Used to prevent the SAS Stored Process Server&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;from returning &lt;/span&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;the message &lt;/span&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;that no output was&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;generated for situations where &lt;/span&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;there is no data.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;Can be called immediately after reporting code,&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;using the same &lt;/span&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;input data &lt;/span&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;set, to produce the&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;custom message if the input &lt;/span&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;data &lt;/span&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;is empty.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;---------------------------------------------*/&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;data nodata;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;length msg $128;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;if lr then&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;do; /* no data */&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; msg = symget("Message");&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;output;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;end; /* no data */&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;set &amp;amp;data end=lr;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;stop;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;run;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;proc report data = nodata nowd&lt;/span&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;&amp;nbsp;columns msg;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;&amp;nbsp;&lt;/span&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;define msg / display ' ';&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;run;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;, Courier, monospace;"&gt;%mend noDataFoundMessage;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1116546449646002174-5119064291858322716?l=hcsbi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hcsbi.blogspot.com/feeds/5119064291858322716/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hcsbi.blogspot.com/2011/04/this-page-intentionally-left-blank.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1116546449646002174/posts/default/5119064291858322716'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1116546449646002174/posts/default/5119064291858322716'/><link rel='alternate' type='text/html' href='http://hcsbi.blogspot.com/2011/04/this-page-intentionally-left-blank.html' title='This Page Intentionally Left Blank'/><author><name>DonH</name><uri>http://www.blogger.com/profile/12347027394201327945</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1116546449646002174.post-8836313768214165280</id><published>2010-07-12T08:00:00.003-04:00</published><updated>2010-07-12T08:00:09.720-04:00</updated><title type='text'>Generating Descriptive Please Wait Messages for Long Running Stored Processes</title><content type='html'>&lt;div&gt;Well, its been a while since I posted to my blog. And to acknowledge this, I choose a topic that is all about being patient ;-).&lt;br /&gt;&lt;br /&gt;Most everyone who has built a web application that takes more than a split second to run knows that users want immeditate feedback. That is why many sites generating spinning logos (and other amusing things) to alert users that progress is being made.&lt;br /&gt;&lt;br /&gt;Such functionality is fairly straigtforward to implement in SAS Stored Processes. So here goes.&lt;br /&gt;&lt;br /&gt;First check out &lt;a href="http://www.hcsbi.com:65432/SASStoredProcess/do?_program=/Projects/Tools/runMacro&amp;amp;macroToRun=pleaseWaitExample&amp;amp;_odsstyle=sasweb"&gt;an example&lt;/a&gt;, running on my server. This example interleaves a &lt;strong&gt;DATA&lt;/strong&gt; step containing a &lt;strong&gt;SLEEP&lt;/strong&gt; function call with a call to my &lt;strong&gt;pleaseWait&lt;/strong&gt; macro.&lt;br /&gt;&lt;br /&gt;So how'd I do that? It was actually not that hard. I took advantage of some HTML style/display attributes that can be controlled thru JavaScript combined with the &lt;strong&gt;SAS ODS HTML TEXT=&lt;/strong&gt; statement. Here is a little code snippet from my &lt;em&gt;&lt;strong&gt;pleaseWait&lt;/strong&gt;&lt;/em&gt; macro that illustrates how it is done:&lt;br /&gt;&lt;br /&gt;&lt;font size="3"&gt;&lt;font size="2" face="courier new"&gt;ods html text = "&amp;lt;span id=""pleaseWait&amp;amp;pleaseWaitCounter""&amp;gt;";&lt;br /&gt;proc report data = pleaseWait nowd;&lt;br /&gt; title "&amp;amp;message";&lt;br /&gt; columns msg;&lt;br /&gt; define msg / " ";&lt;br /&gt;run;&lt;br /&gt;ods html text = '&amp;lt;/span&amp;gt;';&lt;/font&gt;&lt;br /&gt;&lt;/font&gt;&lt;br /&gt;Some details:&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;The &lt;em&gt;pleaseWait&lt;/em&gt; SAS data set is a single observation SAS data set where the informative message is the value of the macro variable &amp;amp;message.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The value of the data step variable &lt;em&gt;msg&lt;/em&gt; is the logo I want to display.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The macro variable reference &lt;em&gt;&amp;amp;pleaseWaitCounter&lt;/em&gt; is used to allow for multiple status messages to be generated with each message replacing the prior one.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;I tried to do this all with the &lt;strong&gt;ODS HTML TEXT&lt;/strong&gt; (i.e. without a &lt;strong&gt;PROC&lt;/strong&gt; step), but it seems that &lt;strong&gt;ODS&lt;/strong&gt; output in not streamed back to browser until there is a &lt;strong&gt;DATA&lt;/strong&gt; and/or &lt;strong&gt;PROC&lt;/strong&gt; step.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;Check out the &lt;a href="http://www.sascommunity.org/wiki/Generating_Descriptive_Please_Wait_Messages_for_Long_Running_Stored_Processes"&gt;article&lt;/a&gt; on &lt;a href="http://www.sascommunity.org/"&gt;sasCommunity.org&lt;/a&gt; for the source code for the macro and the sample call. Feel free to comment here or on the sasCommunity &lt;a href="http://www.sascommunity.org/wiki/Talk:Generating_Descriptive_Please_Wait_Messages_for_Long_Running_Stored_Processes"&gt;discussion page&lt;/a&gt; if you have questions or want more details.&lt;/p&gt;&lt;br /&gt;&lt;em&gt;&lt;/em&gt;&lt;em&gt;&lt;/em&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1116546449646002174-8836313768214165280?l=hcsbi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hcsbi.blogspot.com/feeds/8836313768214165280/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hcsbi.blogspot.com/2010/07/generating-descriptive-please-wait.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1116546449646002174/posts/default/8836313768214165280'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1116546449646002174/posts/default/8836313768214165280'/><link rel='alternate' type='text/html' href='http://hcsbi.blogspot.com/2010/07/generating-descriptive-please-wait.html' title='Generating Descriptive Please Wait Messages for Long Running Stored Processes'/><author><name>DonH</name><uri>http://www.blogger.com/profile/12347027394201327945</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1116546449646002174.post-5249387818231246121</id><published>2010-03-03T07:10:00.010-05:00</published><updated>2010-03-03T07:10:00.301-05:00</updated><title type='text'>HTML, PDF, RTF (and more) all at the same time.</title><content type='html'>&lt;i&gt;or . . . when is a session not really a session.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;In my &lt;a href="http://hcsbi.blogspot.com/2010/02/sessions-double-edged-sword.html"&gt;last post&lt;/a&gt; I illustrated explicity creating and using a session. Of course as with all things &lt;b&gt;SAS&lt;/b&gt; related, that is not the only way to create a session!&lt;br /&gt;&lt;br /&gt;In order for ODS to support creating html page with mixed text and graphics (e.g., a PROC PRINT and a PROC GCHART), ODS uses &lt;em&gt;lightweight&lt;/em&gt; sessions. These are sessions without a lot of the overhead of a full session, but which allow a program to write some content, e.g., in the case of ODS, perhaps a graph where the generated HTML contains a link that &lt;b&gt;&lt;em&gt;replays&lt;/em&gt;&lt;/b&gt; the graph. So while it looks to the user like they are getting a table and graph in one request, there are actually two requests. Run this sample &lt;a href="http://www.hcsbi.com/scripts/broker.exe?_debug=0&amp;_service=appdisp&amp;_program=saspress.chapter8.tableAndGraph.source"&gt;Table and Graph&lt;/a&gt; and take a look at the generated HTML for the &lt;b&gt;img&lt;/b&gt; tag.&lt;br /&gt;&lt;br /&gt;&lt;em&gt;Note that this example is actually a SAS/IntrNet sample from my &lt;a href="http://www.sascommunity.org/wiki/Building_Web_Applications_with_SAS/IntrNet:_A_Guide_to_the_Application_Dispatcher"&gt;SAS Press Book&lt;/a&gt; . . . . . . but the Stored Process Server works the same way!&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;The generated link uses the &lt;em&gt;replay&lt;/em&gt; program to do that. Upon a review of the generated HTML (just do a View Source in your browser) you see that the &lt;em&gt;replayed&lt;/em&gt; graph is actually an entry in a SAS catalog. Well, it turns out that whenever you submit a request for the Stored Process Server to run a program, SAS creates a catalog and the catalog name is available to your program as the macro variable reference &lt;strong&gt;&lt;em&gt;&amp;amp;_tmpcat&lt;/em&gt;&lt;/strong&gt;.&lt;br /&gt;&lt;br /&gt;And now it gets even more interesting because simply by writing something to that catalog causes SAS to create the &lt;em&gt;lightweight&lt;/em&gt; session.&lt;br /&gt;&lt;br /&gt;Now lets consider the case where we want to generate an HTML report, but you also want to provide a way for a user to access a printable version of the report (e.g., a PDF file) or an easily editable version (e.g., an RTF file) without having to rerun the reporting code. A lightweight session can be used to so that ODS will write PDF and RTF versions to the &lt;strong&gt;&lt;em&gt;&amp;amp;_tmpcat&lt;/em&gt;&lt;/strong&gt; catalog while generating/streaming the HTML version back to the users browser.&lt;br /&gt;&lt;br /&gt;Just include statements like this in your program:&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;filename pd catalog "&amp;_tmpcat..shoes.pdf";&lt;br /&gt;&amp;nbsp;&amp;nbsp;filename rt catalog "&amp;_tmpcat..shoes.rtf";&lt;br /&gt;&lt;br /&gt;and then use the stpBegin macro for the HTML version and explicit ODS statements for the PDF and RTF versions:&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;%stpBegin&lt;br /&gt;&amp;nbsp;&amp;nbsp;ods pdf file = pd style = sasweb notoc;&lt;br /&gt;&amp;nbsp;&amp;nbsp;ods rtf file = rt style=sasweb;&lt;br /&gt;&lt;br /&gt;then include whatever your reporting code is (e.g., PRINT, TABULATE, REPORT, etc.). &lt;br /&gt;&lt;br /&gt;We now need to include some HTML to provide the hyperlinks to that will &lt;em&gt;replay&lt;/em&gt; the PDF and/or RTF versions. The following DATA steps creates macro variables whose values are the needed HTML:&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;data _null_;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;call symput('replayPDF','''&amp;lt;center&amp;gt;&amp;lt;a href="'||&amp;_replay||'shoes.pdf"&amp;gt;Printable (PDF)&amp;lt;/a&amp;gt;&amp;lt;/center&amp;gt;''');&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;call symput('replayRTF','''&amp;lt;center&amp;gt;&amp;lt;a href="'||&amp;_replay||'shoes.rtf"&gt;Editable (RTF)&amp;lt;/a&amp;gt;&amp;lt;/center&amp;gt;''');&lt;br /&gt;&amp;nbsp;&amp;nbsp;run;&lt;br /&gt;&lt;br /&gt;&lt;em&gt;and, yea, I know the quoting is &lt;b&gt;UGLY&lt;/b&gt;. I promise I'll blog about better ways to do this at some point in the future ;-).&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;and these ODS HTML statements generate the needed links in the HTML version.&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;ods html text = &amp;replayPDF;&lt;br /&gt;&amp;nbsp;&amp;nbsp;ods html text = &amp;replayRTF;&lt;br /&gt; &lt;br /&gt;And, as a bonus, since the ODS HTML statement does not write any content to the PDF or ODS destinations, those versions of the reports do not contain the hyperlink text!&lt;br /&gt;&lt;br /&gt;And since the %stpEnd macro generates an&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;ODS _ALL_ close;&lt;br /&gt;&lt;br /&gt;statement, it will close all of the output destinations for you.&lt;br /&gt;&lt;br /&gt;Feel free to check out this sample on my &lt;a href="http://www.hcsbi.com:65432/SASStoredProcess/do?_program=/Projects/Tools/runMacro&amp;macroToRun=lightweightsessionsexample&amp;_odsstyle=html"&gt;server&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;And, of course, there are more examples of sessions and lightweight sessions in &lt;a href="http://www.sascommunity.org/wiki/Building_Web_Applications_with_SAS/IntrNet:_A_Guide_to_the_Application_Dispatcher"&gt;Building Web Applications with SAS/IntrNet&amp;reg;: A Guide to the Application Dispatcher&lt;/a&gt;. And they work pretty much the same way in the Stored Process Server (with the exception of the one issue highlighted in my PRIOR POSTING).&lt;br /&gt;&lt;br /&gt;Happy Sessioning ;-).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1116546449646002174-5249387818231246121?l=hcsbi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hcsbi.blogspot.com/feeds/5249387818231246121/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hcsbi.blogspot.com/2010/03/html-pdf-rtf-and-more-all-at-same-time.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1116546449646002174/posts/default/5249387818231246121'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1116546449646002174/posts/default/5249387818231246121'/><link rel='alternate' type='text/html' href='http://hcsbi.blogspot.com/2010/03/html-pdf-rtf-and-more-all-at-same-time.html' title='HTML, PDF, RTF (and more) all at the same time.'/><author><name>DonH</name><uri>http://www.blogger.com/profile/12347027394201327945</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1116546449646002174.post-6067048252343575787</id><published>2010-02-18T05:00:00.000-05:00</published><updated>2010-02-18T08:45:44.732-05:00</updated><title type='text'>Sessions - A double edged sword</title><content type='html'>In my &lt;a href="http://hcsbi.blogspot.com/2010/02/work-doesnt-work-same-way.html"&gt;previous post&lt;/a&gt; (which was longer ago than I'd like - but maybe I can blame the US East Coast Blizzard&lt;strong&gt;s&lt;/strong&gt;) I blogged about using Sessions as a surrogate for some of the things we &lt;em&gt;old-style&lt;/em&gt; SAS programmers used the &lt;strong&gt;WORK&lt;/strong&gt; library for.&lt;br /&gt;&lt;br /&gt;One of the things that many of us overlook when delivering SAS content over the web is that each request has to be quick - users get very impatient if they have to wait too long. So long-running programs with lots of steps should not just be packaged up as Stored Processes. They need to be broken down into pieces/parts that can be run sequentially - just like one can run a series of &lt;strong&gt;DATA/PROC&lt;/strong&gt; steps in an interactive SAS session. And that is where sessions can come into play - we can use a session to save data sets (and macro variables) from one request to the next). So the Sessions &lt;strong&gt;SAVE&lt;/strong&gt; library becomes the replacement/alternative for &lt;strong&gt;WORK&lt;/strong&gt;.&lt;br /&gt;&lt;br /&gt;So here goes with a little tutorial on creating a Stored Process that uses sessions. Let me begin by saying that I will of course be packaging this as a macro so I can leverage my &lt;a href="http://hcsbi.blogspot.com/2010/01/useful-parametersextensions-for.html"&gt;runMacro Stored Process&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The following macro code shows a simple template for how to use sessions. Note how the sysfunc macro is used to invoke the libref function which checks if the &lt;strong&gt;SAVE&lt;/strong&gt; library exists. Since the &lt;strong&gt;SAVE&lt;/strong&gt; library is created when the sesssion is created and is made available when reconnecting to a session, that is a convenient way to check if we already have a session.&lt;br /&gt;&lt;pre&gt;%macro sessionsExample;&lt;br /&gt; %global save_Session_Created;&lt;br /&gt; %if %sysfunc(libref(save)) ne 0 %then&lt;br /&gt; %do;  /* session does not exist - create it */&lt;br /&gt;&lt;br /&gt; %end;  /* session does not exist - create it */&lt;br /&gt; %else&lt;br /&gt; %do;  /* reconnecting to the session */&lt;br /&gt;&lt;br /&gt; %end; /* reconnecting to the session */&lt;br /&gt;%mend sessionsExample;&lt;/pre&gt;&lt;br /&gt;In the first %do-%end block we add some code to create a session: &lt;pre&gt; %let rc = %sysfunc(stpsrv_session(create));&lt;/pre&gt;and create a macro variable to be saved (note the prefix &lt;strong&gt;save_&lt;/strong&gt;)&lt;pre&gt; %let save_Session_Created = %sysfunc(datetime(),datetime.);&lt;/pre&gt;and, in this case some sample code to perhaps access a large table and summarize it - so we only have to do that once (and please forgive me for &lt;em&gt;cheating&lt;/em&gt; :-) here and using a small &lt;strong&gt;SASHELP&lt;/strong&gt; data set).&lt;pre&gt;proc summary data = sashelp.shoes nway;&lt;br /&gt; class product;&lt;br /&gt; var stores sales returns inventory;&lt;br /&gt; output out=save.SummaryTable(drop=_freq_ _type_) sum=;&lt;br /&gt;run;&lt;/pre&gt;We then list the data and use a title to generate a hyperlink to reconnect to the session.&lt;pre&gt;proc print data = save.SummaryTable;&lt;br /&gt; title "Session Created at &amp;save_Session_Created";&lt;br /&gt; title2 '&amp;lt;a href="' "&amp;amp;_thisSession" '&amp;amp;_program=' "&amp;amp;_program" '&amp;amp;macroToRun=' "&amp;amp;macroTorun" '"&amp;gt;Reconnect&amp;lt;/a&amp;gt;';&lt;br /&gt;run;&lt;/pre&gt;The macro variable &lt;strong&gt;_thisSession&lt;/strong&gt; is the key here - this is what causes the request to connect back to the same session as described in this SAS &lt;a href="http://support.sas.com/rnd/itech/doc9/dev_guide/stprocess/sessions.html"&gt;9.1.3 Doc&lt;/a&gt;. (and it works pretty much the same in 9.2)&lt;br /&gt;&lt;br /&gt;And in the code where we are reconnecting, we can access the macro variables and the data we saved in the &lt;strong&gt;SAVE&lt;/strong&gt; library, e.g., &lt;pre&gt;proc report data = save.SummaryTable;&lt;br /&gt; columns product stores sales returns inventory;&lt;br /&gt; rbreak after / summarize;&lt;br /&gt; title "Reconnecting at %sysfunc(datetime(),datetime.) to Session Created at &amp;amp;save_Session_Created";&lt;br /&gt; footnote '&amp;lt;a href="' "&amp;amp;_thisSession" '&amp;amp;_program=' "&amp;amp;_program" '&amp;amp;macroToRun=' "&amp;amp;macroTorun" '"&amp;gt;Reconnect Again&amp;lt;/a&amp;gt;&lt;br /&gt;run;&lt;/pre&gt;Feel free to &lt;a href="http://www.hcsbi.com:65432/SASStoredProcess/do?_program=/Projects/Tools/runMacro&amp;macroToRun=sessionsExample&amp;_odsstyle=html"&gt;try it on on my server&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;There are lots of other example of sessions described in my &lt;a href="http://www.sascommunity.org/wiki/Building_Web_Applications_with_SAS/IntrNet:_A_Guide_to_the_Application_Dispatcher"&gt;SAS Press Book&lt;/a&gt;. They work pretty much the same way in the SAS 9 Stored Process server with a few minor differences described on this page about &lt;a href="http://support.sas.com/rnd/itech/doc9/dev_guide/stprocess/inet2stp.html"&gt;upgrading SAS/IntrNet program to Stored Processes&lt;/a&gt; (the most notable is the format change for the &lt;strong&gt;_REPLAY&lt;/strong&gt; macro variable).&lt;br /&gt;&lt;br /&gt;So by now, if you made it this far :-), you are probably confused by the reference to the &lt;em&gt;double edged sword&lt;/em&gt;. Check out the &lt;a href="http://support.sas.com/rnd/itech/doc9/dev_guide/stprocess/sessions.html"&gt;limitations at the bottom of this page&lt;/a&gt; - overuse of sessions can seriously impact performance. So only use em when you need em!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1116546449646002174-6067048252343575787?l=hcsbi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hcsbi.blogspot.com/feeds/6067048252343575787/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hcsbi.blogspot.com/2010/02/sessions-double-edged-sword.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1116546449646002174/posts/default/6067048252343575787'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1116546449646002174/posts/default/6067048252343575787'/><link rel='alternate' type='text/html' href='http://hcsbi.blogspot.com/2010/02/sessions-double-edged-sword.html' title='Sessions - A double edged sword'/><author><name>DonH</name><uri>http://www.blogger.com/profile/12347027394201327945</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1116546449646002174.post-3639203093222572483</id><published>2010-02-02T08:15:00.001-05:00</published><updated>2010-02-02T08:15:00.171-05:00</updated><title type='text'>WORK doesn't work the same way</title><content type='html'>So hows that for a &lt;em&gt;double entendre&lt;/em&gt;?&lt;br /&gt;&lt;br /&gt;One of the biggest challenges traditional SAS users have trouble with is the fact that &lt;strong&gt;WORK&lt;/strong&gt; does not work the same way in BI/Web applications. Such SAS users are used to the work library containing all sorts of data sets and catalogs that are easily accessible later in the program (either in an interactive SAS session or a long batch job). Most BI tasks however are (and should be) structured as a series of short tasks. This is exactly where some SAS users get tripped up when building BI/Web based reports - each request starts with a brand new clean/emply &lt;strong&gt;WORK&lt;/strong&gt; library. This is because the Web is a stateless environment – each request knows nothing about any prior request. This creates a simple environment in that each request is completely self contained. However, quite often it is desirable and necessary to maintain certain information from one request to the next (i.e. do what the &lt;strong&gt;WORK&lt;/strong&gt; library does). This is known as maintaining state.&lt;br /&gt;&lt;br /&gt;The good news is that that there are a number of ways to deal with this. The Stored Process Server (and the SAS/IntrNet Application Dispatcher) support the creation and use of Sessions as a way to share data and macro variables from one request to the next. A Session is simply the saving of data and parameters from one request to the next. Sessions provide the ability to save library members (data sets and catalogs) and macro variables. The program explicitly specifies what to save and restore.&lt;br /&gt;&lt;br /&gt;The mechanics of using Sessions are straightforward. Once a session has been created, a library named &lt;strong&gt;SAVE&lt;/strong&gt; is created. By creating or copying data sets and catalogs to this library, the user program can rely on them being there the next time a request is made that uses this session. For global macro variables, at the completion of any request that uses sessions, all the global macro variables whose name begin with SAVE_ are saved. At the beginning of any future request, they are restored.&lt;br /&gt;&lt;br /&gt;And sometimes SAS will create sessions automatically. For example, when ODS is used to create an HTML page containing a graph, ODS will create a &lt;strong&gt;&lt;em&gt;lightweight session&lt;/em&gt;&lt;/strong&gt;. Since a single web request can only return one file, ODS will create the additional files and save them in SAS catalog entries. For an HTML page with an embedded graph, the graph (or graphs) will be created and saved as a graphic (e.g., gif or jpeg) entry in a catalog.  For a frame, the table of contents file and the body file are saved in html entries in such a catalog. These entries are returned to the user’s browser by a link that reconnects to the session to replay the entry.&lt;br /&gt;&lt;br /&gt;Check back later for some examples on creating and using sessions:&lt;ul&gt;&lt;li&gt;the mechanics of creation a session&lt;li&gt;using lightweight sessions so you can generate, for example, HTML, PDF and RTF output in &lt;em&gt;one request&lt;/em&gt;&lt;li&gt;creating a session to extract/summarize data once so multiple reports can be generated from it.&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1116546449646002174-3639203093222572483?l=hcsbi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hcsbi.blogspot.com/feeds/3639203093222572483/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hcsbi.blogspot.com/2010/02/work-doesnt-work-same-way.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1116546449646002174/posts/default/3639203093222572483'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1116546449646002174/posts/default/3639203093222572483'/><link rel='alternate' type='text/html' href='http://hcsbi.blogspot.com/2010/02/work-doesnt-work-same-way.html' title='WORK doesn&apos;t work the same way'/><author><name>DonH</name><uri>http://www.blogger.com/profile/12347027394201327945</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1116546449646002174.post-71838104157677588</id><published>2010-01-26T09:30:00.002-05:00</published><updated>2010-01-26T09:30:00.192-05:00</updated><title type='text'>An autoexec facility for the Stored Process Server</title><content type='html'>The SAS 9.2 Stored Process Server includes an option to specify the name of a SAS program that is to be run before any request - this is conceptually similar to the &lt;strong&gt;autoexec&lt;/strong&gt; facility that has existed in SAS back to it's Jurassic days.&lt;br /&gt;&lt;br /&gt;And, as an added bonus, there is also an option to specify the name of a program to run after each stored process. This facility provides the facility to do many of the functions described in Chapter 9 of my SAS Press Book, &lt;a href="http://www.sascommunity.org/wiki/Building_Web_Applications_with_SAS/IntrNet:_A_Guide_to_the_Application_Dispatcher"&gt;Building Web Applications with SAS/IntrNet&amp;reg;: A Guide to the Application Dispatcher&lt;/a&gt;, such as defining the list of available libraries based on who the user is, etc.&lt;br /&gt;&lt;br /&gt;&lt;font color="red"&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: Do &lt;strong&gt;NOT&lt;/strong&gt; try the examples that utilize &lt;strong&gt;ENDSAS;&lt;/strong&gt; as that statement does not do the same thing in the Stored Process Server as it did in the SAS/IntrNet Application Dispatcher where it simply ended the current request. In the Stored Process Server it will shut down the Stored Process Server itself.&lt;/font&gt;&lt;br /&gt;&lt;br /&gt;Just follow these simple &lt;a href="http://support.sas.com/documentation/cdl/en/biasag/61237/HTML/default/stpopts.htm"&gt;instructions&lt;/a&gt; to specify the program to run before/after each Stored Process.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1116546449646002174-71838104157677588?l=hcsbi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hcsbi.blogspot.com/feeds/71838104157677588/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hcsbi.blogspot.com/2010/01/autoexec-facility-for-stored-process.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1116546449646002174/posts/default/71838104157677588'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1116546449646002174/posts/default/71838104157677588'/><link rel='alternate' type='text/html' href='http://hcsbi.blogspot.com/2010/01/autoexec-facility-for-stored-process.html' title='An autoexec facility for the Stored Process Server'/><author><name>DonH</name><uri>http://www.blogger.com/profile/12347027394201327945</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1116546449646002174.post-5975529524676585398</id><published>2010-01-21T09:30:00.000-05:00</published><updated>2010-01-21T09:30:00.673-05:00</updated><title type='text'>When was this report generated?</title><content type='html'>It is almost always a good idea to include in any generated output report some indication of when the report was generated. Not only does it provide context, but when viewing reports generated dynamically by, say, the SAS/IntrNet Application Dispatcher or the Stored Process server, it can also help to confirm that the user is viewing a current version rather than a version that has been cached by, say, their browser or a proxy server.&lt;br /&gt;&lt;br /&gt;SAS provides a number of tools to include the date and/or time in the generated output. I like to include the values in a footnote and use the %sysfunc function to get the current date and/or time. And, of course, for the sake of consistency and ease of use, I package this code as a macro.&lt;br /&gt;&lt;br /&gt;Here is a sample macro:&lt;br /&gt;&lt;br /&gt;%macro generatedAt&lt;br /&gt;       (text=Generated At,&lt;br /&gt;        datefmt=worddate.,&lt;br /&gt; timefmt=timeampm7.,&lt;br /&gt; separator=on&lt;br /&gt;       );&lt;br /&gt;&lt;br /&gt; %local dt tm;&lt;br /&gt; %if %superQ(datefmt) ne %then %let dt = %sysfunc(date(),&amp;datefmt);&lt;br /&gt; %if %superQ(timefmt) ne %then %let tm = %sysfunc(time(),&amp;timefmt);&lt;br /&gt;&lt;br /&gt; &amp;text &amp;tm &amp;separator &amp;dt&lt;br /&gt;&lt;br /&gt;%mend generatedAt;&lt;br /&gt;&lt;br /&gt;The macro generates text that can be used in a &lt;strong&gt;title&lt;/strong&gt; or &lt;strong&gt;footnote&lt;/strong&gt; statement; or even in a &lt;strong&gt;put&lt;/strong&gt; statement. This version of the macro allows you to specifiy:&lt;ul&gt;&lt;li&gt;&lt;strong&gt;text&lt;/strong&gt;: the text to display before the date/time&lt;li&gt;&lt;strong&gt;datefmt&lt;/strong&gt;: the SAS date format to use for the date format. If a null/blank value is specified, the date is not included&lt;li&gt;&lt;strong&gt;timefmt&lt;/strong&gt;: the SAS time format to use for the time format. If a null/blank value is specified, the time is not included&lt;li&gt;&lt;strong&gt;separator&lt;/strong&gt;: the text to separate the date and time display&lt;/ul&gt;There are lots of other options that one could consider (e.g., list the the date and then the time or vice versa), but I've found that most of my clients want consistency and so I simply the customize the macro to generate the text to be exactly the way they want it.&lt;br /&gt;&lt;br /&gt;Now all I have to do is include the macro call in, for example, a &lt;strong&gt;footnote&lt;/strong&gt; statement:&lt;br /&gt;&lt;br /&gt;proc print data = sashelp.class;&lt;br /&gt; title 'The ever popular CLASS data set';&lt;br /&gt; footnote j=right h=1 "%generatedAt";&lt;br /&gt;run;&lt;br /&gt;&lt;br /&gt;Note the user of the j (justify) and h (height) attributes. Check out &lt;a href="http://www.sascommunity.org/wiki/Controlling_text_height_in_titles_and_footnotes"&gt;Controlling text height in titles and footnotes&lt;/a&gt; for some other examples of controlling the format in &lt;strong&gt;title&lt;/strong&gt; and &lt;strong&gt;footnote&lt;/strong&gt; statements.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1116546449646002174-5975529524676585398?l=hcsbi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hcsbi.blogspot.com/feeds/5975529524676585398/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hcsbi.blogspot.com/2010/01/when-was-this-report-generated.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1116546449646002174/posts/default/5975529524676585398'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1116546449646002174/posts/default/5975529524676585398'/><link rel='alternate' type='text/html' href='http://hcsbi.blogspot.com/2010/01/when-was-this-report-generated.html' title='When was this report generated?'/><author><name>DonH</name><uri>http://www.blogger.com/profile/12347027394201327945</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1116546449646002174.post-4271541266149272011</id><published>2010-01-18T09:15:00.001-05:00</published><updated>2010-01-18T09:15:00.542-05:00</updated><title type='text'>Useful parameters/extensions for the runMacro Stored Process</title><content type='html'>Before leaving the &lt;strong&gt;runMacro&lt;/strong&gt; Stored Process and moving on to other topics, lets add some features to our Stored Process. In &lt;a href="http://hcsbi.blogspot.com/2009/12/dressing-up-proc-report.html"&gt;Dressing Up PROC REPORT&lt;/a&gt; we saw how the SAS TableEditor tagset added a lot of capability to our reports. So lets add support for the &lt;a href="http://support.sas.com/rnd/base/ods/odsmarkup/tableeditor/index.html"&gt;TableEditor&lt;/a&gt; tagset, the &lt;a href="http://support.sas.com/rnd/base/ods/odsmarkup/excelxp_help.html"&gt;ExcelXP&lt;/a&gt; tagset, and simple HTML displayed as if it were an Excel file.&lt;br /&gt;&lt;br /&gt;The &lt;strong&gt;stpBegin&lt;/strong&gt; macro does a lot for us, but for some of the customizations we will need to help it out a little by adding some logic in a &lt;strong&gt;DATA&lt;/strong&gt; step before calling &lt;strong&gt;stpBegin&lt;/strong&gt;.&lt;br /&gt;&lt;br /&gt;For HTML opened in Excel, we just need to make sure that the &lt;em&gt;&lt;a href="http://support.sas.com/rnd/itech/doc9/dev_guide/stprocess/httphead.html#Content-type"&gt;Content-type&lt;/a&gt;&lt;/em&gt; header for Excel is used, e.g.,&lt;br /&gt;&lt;br /&gt;rc = stpsrv_header('content-type','application/vnd.ms-excel');&lt;br /&gt;&lt;br /&gt;We will do this by specifying a value of &lt;strong&gt;EXCEL&lt;/strong&gt; for &lt;strong&gt;_odsDest&lt;/strong&gt; (which we reset to HTML after generating the Content-type header).&lt;br /&gt;&lt;br /&gt;We need the same &lt;em&gt;Content-type&lt;/em&gt; header for ExcelXP. But due to an issue with how Office 2007 works, we also need to provide a &lt;em&gt;Content-disposition&lt;/em&gt; header that specifies it is an XML file, e.g.,&lt;br /&gt;&lt;br /&gt;rc = stpsrv_header('Content-disposition','attachment; filename="_.xml"');&lt;br /&gt;&lt;br /&gt;Note the use of &lt;strong&gt;_.xml&lt;/strong&gt; as the filename. Our generic Stored Process can't infer a reasonable name, so we just use the _. Of course, you could make this a parameter - but that is left as exercise for the reader ;-).&lt;br /&gt;&lt;br /&gt;For the &lt;strong&gt;TableEditor&lt;/strong&gt; tagset, we just need to pass a few options into the &lt;strong&gt;stpBegin&lt;/strong&gt; macro using the global macro variable &lt;strong&gt;_odsOptions&lt;/strong&gt;. There are lots of parameters the we could use. For the purposes of this example, a few that you will almost certainly want to support are:&lt;ol&gt;&lt;li&gt;pageheight&lt;li&gt;pagewidth&lt;li&gt;sort&lt;li&gt;frozen_rowheaders&lt;li&gt;frozen_headers&lt;/ol&gt;Just create parameters for each of these and then reference the values as macro variables. You should also make sure to specify default values for the Stored Process parameters. The following code will append the values specified to the existing value of &lt;strong&gt;_odsOptions&lt;/strong&gt; (which could also be a Stored Process parameter).&lt;br /&gt;&lt;br /&gt;call symput&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;('_odsOptions',&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;("options(" ||&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;|| strip(symget('_odsOptions')&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;|| " pageheight='&amp;pageheight'"&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;|| " pagewidth='&amp;pagewidth'"&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;|| " sort='&amp;sort'"&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;|| " frozen_rowheaders='&amp;frozen_rowheaders'"&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;|| " frozen_headers='&amp;frozen_headers')"));&lt;br /&gt;&lt;br /&gt;The complete &lt;a href="http://www.sascommunity.org/wiki/The_runMacro_Stored_Process"&gt;runMacro Stored Process&lt;/a&gt; can be found on &lt;a href="http://www.sasCommunity.org"&gt;sasCommunity&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1116546449646002174-4271541266149272011?l=hcsbi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hcsbi.blogspot.com/feeds/4271541266149272011/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hcsbi.blogspot.com/2010/01/useful-parametersextensions-for.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1116546449646002174/posts/default/4271541266149272011'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1116546449646002174/posts/default/4271541266149272011'/><link rel='alternate' type='text/html' href='http://hcsbi.blogspot.com/2010/01/useful-parametersextensions-for.html' title='Useful parameters/extensions for the runMacro Stored Process'/><author><name>DonH</name><uri>http://www.blogger.com/profile/12347027394201327945</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1116546449646002174.post-4110945989873960310</id><published>2010-01-15T09:15:00.001-05:00</published><updated>2010-01-15T09:15:00.436-05:00</updated><title type='text'>A Home for my runMacro Stored Process</title><content type='html'>In my last post I talked about my runMacro Stored Process and one approach for storing/managing the macro code. Now it is time to talk about where to store the stored process code itself.&lt;br /&gt;&lt;br /&gt;In the SAS Metadata (using SAS Management Console, aka &lt;b&gt;SMC&lt;/b&gt;) you can define a logical hierarchy for your Stored Processes which is completely independent of how/where they are physically stored. &lt;br /&gt;&lt;br /&gt;For the physical location on the file system, I like to use a directory structure comparable directory comparable to how Macros are organized for my Stored Processes, e.g.,&lt;br /&gt;&lt;ul&gt;&lt;li&gt;/Projects/ProjectA/storedProcesses&lt;br /&gt;&lt;li&gt;/Projects/ProjectB/storedProcesses&lt;br /&gt;&lt;li&gt;. . .&lt;br /&gt;&lt;li&gt;/Projects/Tools/storedProcesses&lt;br /&gt;&lt;/ul&gt;So, I would stored my &lt;b&gt;runMacro&lt;/b&gt; stored process as &lt;b&gt;/Projects/Tools/storedProcesses/runMacro.sas&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;Given that the folder hiearchy in the SAS Metadata can be different, there are lots of options for packaging the &lt;b&gt;runMacro&lt;/b&gt; Stored Process. Two examples (and there are lots of variations) are described below.&lt;br /&gt;&lt;h3&gt;A single Stored Process&lt;/h3&gt;This option has the advantage that it can be used to run any macro in any of my project (or application) specific autocall libraries. So the location/name of the Stored Process in SMC might be:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;/Projects/Tools/runMacro&lt;/ul&gt;with &lt;b&gt;Project&lt;/b&gt; as a required parameter (perhaps with a default that pointed to the &lt;b&gt;Tools&lt;/b&gt; autocall macro location).&lt;br /&gt;&lt;br /&gt;This has the advantage that one can easily run any macro merely by providing a value for &lt;b&gt;Project&lt;/b&gt; in the hyperlink or HTML form.&lt;br /&gt;&lt;h3&gt;Project Specific Stored Processes&lt;/h3&gt;Recall that the location in the SAS Metadata is completely independent of the physical location of the code. So we can have multiple instances of a &lt;b&gt;runMacro&lt;/b&gt; Stored Process defined in SMC. Specifically, we can create&lt;br /&gt;&lt;ul&gt;&lt;li&gt;/Projects/ProjectA/runMacro&lt;br /&gt;&lt;li&gt;/Projects/ProjectB/runMacro&lt;br /&gt;&lt;/ul&gt;Note that&lt;ul&gt;&lt;li&gt;there is no need for a folder hierarchy that specifies that these are Stored Processes&lt;li&gt;the file extension of sas is not used as that is an artifact of the physical implementation.&lt;li&gt;both Stored Processes can point to the sas program&lt;/ul&gt;Using this option, you would define &lt;b&gt;Project&lt;/b&gt; as a parameter that is not viewable or editable. The instance in the ProjectA folder would have &lt;b&gt;ProjectA&lt;/b&gt; specified as the default value and the instance in the ProjectB folder would have &lt;b&gt;ProjectB&lt;/b&gt; specified as the default value.&lt;br /&gt;&lt;br /&gt;Such an approach allows you to have completely separate macro libraries for the two projects (but with shared macros still available). As a result you can use the security features availble in SMC to specify what users/groups are allowed to run the ProjectA or the ProjectB macros.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1116546449646002174-4110945989873960310?l=hcsbi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hcsbi.blogspot.com/feeds/4110945989873960310/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hcsbi.blogspot.com/2010/01/home-for-my-runmacro-stored-process.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1116546449646002174/posts/default/4110945989873960310'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1116546449646002174/posts/default/4110945989873960310'/><link rel='alternate' type='text/html' href='http://hcsbi.blogspot.com/2010/01/home-for-my-runmacro-stored-process.html' title='A Home for my runMacro Stored Process'/><author><name>DonH</name><uri>http://www.blogger.com/profile/12347027394201327945</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1116546449646002174.post-502006504172155179</id><published>2010-01-13T09:15:00.002-05:00</published><updated>2010-01-13T09:15:00.320-05:00</updated><title type='text'>Where, oh where, do my Macros live?</title><content type='html'>My last post provided a simple SAS program that could be packaged as a Stored Process and that could be used to run any macro. Lets generalize that program a bit - using macro variables.&lt;br /&gt;&lt;br /&gt;But first lets talk a bit about a sample folder hierarchy for storing project code. I like to have a parent directory for projects/applications. As a consultant, typically I deal with projects and so I will have a structure like:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;/Projects/ProjectA&lt;br /&gt;&lt;li&gt;/Projects/ProjectB&lt;br /&gt;&lt;li&gt;. . .&lt;br /&gt;&lt;li&gt;/Projects/Tools&lt;br /&gt;&lt;/ul&gt; where I like to use &lt;b&gt;Tools&lt;/b&gt; for generic facilities that are not project or application specific (some people might want to refer to such things as &lt;b&gt;Shared&lt;/b&gt;).&lt;br /&gt;&lt;br /&gt;One simple way of pointing to the macro library is to include a statement like:&lt;br /&gt;&lt;br /&gt;&lt;font face="courier"&gt;options sasautos=("/Projects/&amp;amp;Project/Macros"&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"/Projects/Tools/Macros"&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sasautos);&lt;/font&gt;&lt;br /&gt;&lt;br /&gt;where the macro variable Project is defined as a parameter to the &lt;b&gt;runMacro&lt;/b&gt; Stored Process. This code concatenates my Project specific autocall macro library, with my generic Tools macro and then the SAS supplied autocall macros.&lt;br /&gt;&lt;br /&gt;So the complete Stored Process source is:&lt;br /&gt;&lt;br /&gt;&lt;font face="courier"&gt;options sasautos=("/Projects/&amp;amp;Project/Macros"&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"/Projects/Tools/Macros"&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sasautos);&lt;br /&gt;*ProcessBody;&lt;br /&gt;%stpBegin;&lt;br /&gt;%put NOTE: Execute macro &amp;amp;macroToRun..;&lt;br /&gt;%&amp;amp;macroToRun&lt;br /&gt;%stpEnd;&lt;br /&gt;&lt;/font&gt;&lt;br /&gt;In my next post, I will address some packaging options so that I can reuse this same source for multiple projects.&lt;br /&gt;&lt;br /&gt;And, &lt;b&gt;&lt;i&gt;BTW&lt;/i&gt;&lt;/b&gt;, did you notice I used the forward slash (/) in my file paths? That is because it &lt;a href="http://www.sascommunity.org/wiki/Tips:Specifying_directory_paths_that_work_on_Window_and_Unix"&gt;works on both Unix and Windows&lt;/a&gt;, thus allowing the code to be migrated across environments more easily.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1116546449646002174-502006504172155179?l=hcsbi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hcsbi.blogspot.com/feeds/502006504172155179/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hcsbi.blogspot.com/2010/01/where-oh-where-do-my-macros-live.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1116546449646002174/posts/default/502006504172155179'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1116546449646002174/posts/default/502006504172155179'/><link rel='alternate' type='text/html' href='http://hcsbi.blogspot.com/2010/01/where-oh-where-do-my-macros-live.html' title='Where, oh where, do my Macros live?'/><author><name>DonH</name><uri>http://www.blogger.com/profile/12347027394201327945</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1116546449646002174.post-6959305573413224623</id><published>2010-01-11T16:41:00.002-05:00</published><updated>2010-01-11T17:09:35.191-05:00</updated><title type='text'>A simple Stored Process to run any Macro</title><content type='html'>&lt;em&gt;&lt;strong&gt;OK. OK. OK.&lt;/strong&gt;&lt;/em&gt; I know I have been negligent is posting the details about this. But I am still trying to wrap my head around doing things in tiny bits and pieces. I promise I will do better from now on ;-)!&lt;br /&gt;&lt;br /&gt;In my last post I talked about creating a simple stored process that could be used to run any macro. So, for example, if you had lots of reports and wanted to make them avaiable from the Stored Process Server you need not create a distinct stored process for each one. &lt;br /&gt;&lt;br /&gt;Create a stored process in an appropriate folder (more on this in later posts) and define the following three parameters:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;b&gt;macroToRun&lt;/b&gt;: the name of the macro that is to be run. Make this a required parameter.&lt;br /&gt;&lt;li&gt;&lt;b&gt;_odsDest&lt;/b&gt;: the ODS Destination (e.g., HTML, PDF, RTF, and so on). Make this a required parameter and give it a default value.&lt;br /&gt;&lt;li&gt;&lt;b&gt;_odsStyle&lt;/b&gt;: the ODS Style to use. This is an optional parameter as SAS will use a default style if not specified.&lt;br /&gt;&lt;/ol&gt;Now, create a sas program as the source for your stored process. Here is a simple version to get you started:&lt;br /&gt;&lt;br /&gt;*ProcessBody;&lt;br /&gt;%stpBegin;&lt;br /&gt;%put NOTE: Execute macro &amp;amp;macroToRun..;&lt;br /&gt;%&amp;amp;macroToRun&lt;br /&gt;%stpEnd;&lt;br /&gt;&lt;br /&gt;When this stored process is executed any values provided in the HTML form or hyperlink are available to the stored process and, thus, to the macro code. So presuming you specify &lt;b&gt;&lt;i&gt;myReport&lt;/i&gt;&lt;/b&gt; as the value of &lt;b&gt;&lt;i&gt;macroToRun&lt;/i&gt;&lt;/b&gt;, the SAS wordscanner will parse&lt;br /&gt;&lt;br /&gt;%&amp;amp;macroToRun&lt;br /&gt;&lt;br /&gt;and resolve it to:&lt;br /&gt;&lt;br /&gt;%myReport&lt;br /&gt;&lt;br /&gt;which will run your report macro assuming the path where it is located is defined to the Stored Process Server (and that will be the subject of an upcoming post).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1116546449646002174-6959305573413224623?l=hcsbi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hcsbi.blogspot.com/feeds/6959305573413224623/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hcsbi.blogspot.com/2010/01/simple-stored-process-to-run-any-macro.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1116546449646002174/posts/default/6959305573413224623'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1116546449646002174/posts/default/6959305573413224623'/><link rel='alternate' type='text/html' href='http://hcsbi.blogspot.com/2010/01/simple-stored-process-to-run-any-macro.html' title='A simple Stored Process to run any Macro'/><author><name>DonH</name><uri>http://www.blogger.com/profile/12347027394201327945</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1116546449646002174.post-6379706580552713377</id><published>2009-12-06T21:35:00.002-05:00</published><updated>2009-12-06T22:30:59.592-05:00</updated><title type='text'>Dressing Up PROC REPORT</title><content type='html'>or &lt;strong&gt;PRINT&lt;/strong&gt;, or &lt;strong&gt;FREQ&lt;/strong&gt; or &lt;strong&gt;TABULATE&lt;/strong&gt;, or . . . . &lt;em&gt;far be it from me to re-start SAS PROC reporting wars ;-)&lt;/em&gt;.&lt;br /&gt;&lt;br /&gt;So lets say you have lots of reporting programs that you would like to make available on the SAS Portal. Since you already have SAS code and data to produce all the reports, you don't want to have to recreate them all using, for example, Web Report Studio (WRS).&lt;br /&gt;&lt;br /&gt;But yet you do need/want some of the cool features that you've seen WRS can do, e.g.,&lt;br /&gt;&lt;ul&gt;&lt;li&gt;frozen column headers as you scroll&lt;/li&gt;&lt;li&gt;sorting the table on any column&lt;/li&gt;&lt;li&gt;and so on, and so on. . . . &lt;/li&gt;&lt;/ul&gt;&lt;p&gt;One solution to this is to use the &lt;a href="http://support.sas.com/rnd/base/ods/odsmarkup/tableeditor/index.html"&gt;SAS TableEditor tagset&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The following simple program (which is run as a &lt;strong&gt;&lt;em&gt;Stored Process&lt;/em&gt;&lt;/strong&gt;) which summarizes/groups the data demonstrates this. Use the above link to find out what the various &lt;em&gt;TableEditor&lt;/em&gt; options in the ods statement do. And, of course, there are lots, lots more options that that do all sorts of &lt;em&gt;kewl&lt;/em&gt; things.&lt;br /&gt;&lt;hr&gt;&lt;br /&gt;&lt;pre&gt;ods tagsets.tableeditor file = _webout&lt;br /&gt;    options(sort="yes" pageheight="300" pagewidth="490"&lt;br /&gt;            background_color="white"&lt;br /&gt;            frozen_headers="yes" frozen_rowheaders="yes");&lt;br /&gt;&lt;br /&gt;proc report data = sashelp.shoes nowd&lt;br /&gt;     style(column) = [font_size=9pt]&lt;br /&gt;     style(header) = [font_size=9pt];&lt;br /&gt; columns Region Stores Sales Inventory Returns;&lt;br /&gt; define Region / group style(Header) = [tagattr="type='String'"];&lt;br /&gt; define _numeric_ / sum style(Header) = [tagattr="type='Numberx'"];&lt;br /&gt; footnote h=1 j=left 'Click on the Column Header to Sort the Rows in the Table';&lt;br /&gt;run;&lt;br /&gt;ods tagsets.tableeditor close;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;hr&gt;&lt;br /&gt;Give it a &lt;a href="http://www.hcsbi.com:65432/Portal"&gt;test run on my server&lt;/a&gt; if you want to see this in action on the SAS Portal.&lt;br /&gt;&lt;br /&gt;My next few posts will talk about how to package code like this using the macro facility in order to make it easy to run them as stored processes. I'll also talk about some other things you can do to make the code a bit &lt;em&gt;glitzier&lt;/em&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1116546449646002174-6379706580552713377?l=hcsbi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hcsbi.blogspot.com/feeds/6379706580552713377/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hcsbi.blogspot.com/2009/12/dressing-up-proc-report.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1116546449646002174/posts/default/6379706580552713377'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1116546449646002174/posts/default/6379706580552713377'/><link rel='alternate' type='text/html' href='http://hcsbi.blogspot.com/2009/12/dressing-up-proc-report.html' title='Dressing Up PROC REPORT'/><author><name>DonH</name><uri>http://www.blogger.com/profile/12347027394201327945</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1116546449646002174.post-8178909732048771890</id><published>2009-12-02T20:45:00.003-05:00</published><updated>2009-12-02T21:39:03.312-05:00</updated><title type='text'>Connecting Two Worlds</title><content type='html'>Since its inception, SAS has always been used by different folks with a completely different perspective on what it is and how it is used (e.g., Statisticians/Data Analyts and users who just crank out reports). Having seen this &lt;em&gt;countably infinite &lt;/em&gt;times, it frustrates me when I see people building applications using the suite of tools available in the BI/EBI platform and commenting that &lt;em&gt;&lt;strong&gt;Gee, I wish I could do XXXX&lt;/strong&gt;&lt;/em&gt; where what they want to do is readily available in the Base SAS toolset.&lt;br /&gt;&lt;br /&gt;Sooooo, I finally decided to create a blog to provide examples and tips to point out the (&lt;em&gt;too many to count&lt;/em&gt;) cases where marrying these two worlds brings a lot to the table.&lt;br /&gt;&lt;br /&gt;This blog is directed to both&lt;br /&gt;&lt;ul&gt;&lt;li&gt;the traditional SAS users who want to learn how their skill-sets might apply in the brave, new BI world.&lt;/li&gt;&lt;li&gt;BI/EBI developers and users, who perhaps only know about cubes, Web Report Studio, and so on. I hope to make them aware of all the great things you can do with simple SAS programs (packaged perhaps as Stored Processes). &lt;/li&gt;&lt;/ul&gt;So let the blogging begin. I know there are plenty of topics that I can address. Just need to find the time and the motivation!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1116546449646002174-8178909732048771890?l=hcsbi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hcsbi.blogspot.com/feeds/8178909732048771890/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hcsbi.blogspot.com/2009/12/connecting-two-worlds.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1116546449646002174/posts/default/8178909732048771890'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1116546449646002174/posts/default/8178909732048771890'/><link rel='alternate' type='text/html' href='http://hcsbi.blogspot.com/2009/12/connecting-two-worlds.html' title='Connecting Two Worlds'/><author><name>DonH</name><uri>http://www.blogger.com/profile/12347027394201327945</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry></feed>
