<?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-1481199104475905802</id><updated>2011-09-28T08:10:58.374-07:00</updated><title type='text'>kpi partners</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://kpipartners.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://kpipartners.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Kurt Wolff</name><uri>http://www.blogger.com/profile/07922051559586431987</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>29</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-1481199104475905802.post-7575143429859540754</id><published>2011-06-10T13:01:00.000-07:00</published><updated>2011-06-10T13:07:30.550-07:00</updated><title type='text'>Handy Date Session Variables</title><content type='html'>It is often convenient to set a number of session variables to capture date values that you use repeatedly in your queries. For example, if you have weeks that end on Saturday, you might want to have the date of the most recent Saturday in a session variable, called perhaps PREVIOUSSATURDAY. You can then use that session variable as the default date value in your queries – for example, “Periods”.”Date” = VALUEOF(NQ_SESSION.PREVIOUSSATURDAY).   &lt;p class="MsoNormal"&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;Assume today is June 10. Using the convention that weeks begin on Sunday (adjust accordingly if that’s not the case for your enterprise), we can think of Current, Previous, and Next weeks.&lt;/p&gt;&lt;p class="MsoNormal"&gt;&lt;a href="http://3.bp.blogspot.com/-63GBlQpqrBc/TfJ4Zxb7ahI/AAAAAAAAA3E/cIvJs_3Yqn4/s1600/Calendar_example.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 325px; height: 260px;" src="http://3.bp.blogspot.com/-63GBlQpqrBc/TfJ4Zxb7ahI/AAAAAAAAA3E/cIvJs_3Yqn4/s400/Calendar_example.png" alt="" id="BLOGGER_PHOTO_ID_5616684069446969874" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;Since you are going to be setting these date variables using physical SQL in initialization blocks, the SQL issued will be specific to the database platform you are using. For Oracle, you could write:&lt;/p&gt;  &lt;p class="MsoNormal"&gt; &lt;/p&gt;  &lt;p style="font-family: courier new;" class="MsoNormal"&gt;&lt;span style="font-size:85%;"&gt;select&lt;/span&gt;&lt;/p&gt;  &lt;p style="font-family: courier new;" class="MsoNormal"&gt;&lt;span style="font-size:85%;"&gt;trunc(sysdate) - to_char(sysdate,'D')+1&lt;/span&gt;&lt;span style="mso-spacerun:yes;font-size:85%;" &gt;   &lt;/span&gt;&lt;span style="font-size:85%;"&gt;CurrentSunday &lt;/span&gt;&lt;/p&gt;  &lt;p style="font-family: courier new;" class="MsoNormal"&gt;&lt;span style="font-size:85%;"&gt;, trunc(sysdate) - to_char(sysdate,'D')+2&lt;/span&gt;&lt;span style="mso-spacerun:yes;font-size:85%;" &gt;  &lt;/span&gt;&lt;span style="font-size:85%;"&gt;CurrentMonday&lt;/span&gt;&lt;/p&gt;  &lt;p style="font-family: courier new;" class="MsoNormal"&gt;&lt;span style="font-size:85%;"&gt;, trunc(sysdate) - to_char(sysdate, 'D')+7 CurrentSaturday&lt;/span&gt;&lt;/p&gt;  &lt;p style="font-family: courier new;" class="MsoNormal"&gt;&lt;span style="font-size:85%;"&gt;, trunc(sysdate) - to_char(sysdate,'D')+8 NextSunday &lt;/span&gt;&lt;/p&gt;  &lt;p style="font-family: courier new;" class="MsoNormal"&gt;&lt;span style="font-size:85%;"&gt;, trunc(sysdate) - to_char(sysdate,'D')&lt;/span&gt;&lt;span style="mso-spacerun:yes;font-size:85%;" &gt;  &lt;/span&gt;&lt;span style="font-size:85%;"&gt;PreviousSaturday &lt;/span&gt;&lt;/p&gt;  &lt;p style="font-family: courier new;" class="MsoNormal"&gt;&lt;span style="font-size:85%;"&gt;, trunc(sysdate) - to_char(sysdate,'D')+2-8&lt;/span&gt;&lt;span style="mso-spacerun:yes;font-size:85%;" &gt;  &lt;/span&gt;&lt;span style="font-size:85%;"&gt;PreviousSunday &lt;/span&gt;&lt;/p&gt;  &lt;p style="font-family: courier new;" class="MsoNormal"&gt;&lt;span style="font-size:85%;"&gt;, trunc(sysdate) - to_char(sysdate,'D')+2-7&lt;/span&gt;&lt;span style="mso-spacerun:yes;font-size:85%;" &gt;  &lt;/span&gt;&lt;span style="font-size:85%;"&gt;PreviousMonday &lt;/span&gt;&lt;/p&gt;  &lt;p style="font-family: courier new;" class="MsoNormal"&gt;&lt;span style="font-size:85%;"&gt;, cast(to_char(trunc(sysdate), 'YYYY') as INT)&lt;/span&gt;&lt;span style="mso-spacerun:yes;font-size:85%;" &gt;  &lt;/span&gt;&lt;span style="font-size:85%;"&gt;CurrentYear &lt;/span&gt;&lt;/p&gt;  &lt;p style="font-family: courier new;" class="MsoNormal"&gt;&lt;span style="font-size:85%;"&gt;, Cast(to_char(trunc(sysdate), 'YYYY')-1 as INT) PreviousYear &lt;/span&gt;&lt;/p&gt;  &lt;p style="font-family: courier new;" class="MsoNormal"&gt;&lt;span style="font-size:85%;"&gt;, add_months(trunc(last_day(sysdate)),-1) + 1 CurrentMonthFirstDay &lt;/span&gt;&lt;/p&gt;  &lt;p style="font-family: courier new;" class="MsoNormal"&gt;&lt;span style="font-size:85%;"&gt;, last_day(trunc(sysdate)) CurrentMonthLastDay&lt;/span&gt;&lt;/p&gt;  &lt;p style="font-family: courier new;" class="MsoNormal"&gt;&lt;span style="font-size:85%;"&gt;, add_months(TRUNC(last_day(sysdate)),-2) + 1 PreviousMonthFirstDay &lt;/span&gt;&lt;/p&gt;  &lt;p style="font-family: courier new;" class="MsoNormal"&gt;&lt;span style="font-size:85%;"&gt;, case when last_day(SYSDATE) = SYSDATE&lt;/span&gt;&lt;span style="mso-spacerun:yes;font-size:85%;" &gt;  &lt;/span&gt;&lt;span style="font-size:85%;"&gt;then TRUNC(SYSDATE) else add_months(TRUNC(last_day(sysdate)),-1) end LASTDAYCOMPLETEMONTH &lt;/span&gt;&lt;/p&gt;  &lt;p style="font-family: courier new;" class="MsoNormal"&gt;&lt;span style="font-size:85%;"&gt;from dual;&lt;/span&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt; &lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;a href="http://1.bp.blogspot.com/-LvGyn0F9qIo/TfJ4aKLE-QI/AAAAAAAAA3M/rsWd8_HWf_g/s1600/Results1.png"&gt;&lt;img style="cursor: pointer; width: 579px; height: 36px;" src="http://1.bp.blogspot.com/-LvGyn0F9qIo/TfJ4aKLE-QI/AAAAAAAAA3M/rsWd8_HWf_g/s400/Results1.png" alt="" id="BLOGGER_PHOTO_ID_5616684076087179522" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p class="MsoNormal"&gt;&lt;a href="http://2.bp.blogspot.com/-Ag2nWMUrwn0/TfJ4asjD84I/AAAAAAAAA3U/zLWjRir-KrU/s1600/Results2.png"&gt;&lt;img style="cursor: pointer; width: 575px; height: 35px;" src="http://2.bp.blogspot.com/-Ag2nWMUrwn0/TfJ4asjD84I/AAAAAAAAA3U/zLWjRir-KrU/s400/Results2.png" alt="" id="BLOGGER_PHOTO_ID_5616684085314581378" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p class="MsoNormal"&gt;If you are using a calendar that’s different from the normal “Gregorian” calendar (i.e. a fiscal calendar) that you have stored in a Periods table, you can write the analogous SQL for that calendar. You won't be able to use the Oracle date functions for many of the values you want, but you can still write the SQL to return the values according to the fiscal periods in your calendar using different methods. &lt;br /&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1481199104475905802-7575143429859540754?l=kpipartners.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/7575143429859540754'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/7575143429859540754'/><link rel='alternate' type='text/html' href='http://kpipartners.blogspot.com/2011/06/handy-date-session-variables.html' title='Handy Date Session Variables'/><author><name>Kurt Wolff</name><uri>http://www.blogger.com/profile/07922051559586431987</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><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-63GBlQpqrBc/TfJ4Zxb7ahI/AAAAAAAAA3E/cIvJs_3Yqn4/s72-c/Calendar_example.png' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-1481199104475905802.post-2696473186536186526</id><published>2011-04-01T13:18:00.000-07:00</published><updated>2011-04-01T13:53:21.545-07:00</updated><title type='text'>Selecting a Single Value and Showing a Range</title><content type='html'>This post shows how you can use presentation variables to select a single value in a dashboard prompt and produce a result set that includes a range of values. For example, you could select a single year in a prompt and show data for that year and the preceding three years.&lt;br /&gt;&lt;a href="http://3.bp.blogspot.com/-shJxmL7guc8/TZY4G2Bv8vI/AAAAAAAAA1w/VCrW9J-dea8/s1600/x1.jpg"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 172px; height: 261px;" src="http://3.bp.blogspot.com/-shJxmL7guc8/TZY4G2Bv8vI/AAAAAAAAA1w/VCrW9J-dea8/s400/x1.jpg" alt="" id="BLOGGER_PHOTO_ID_5590717677660271346" border="0" /&gt;&lt;/a&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/-SUVbwzPrsAk/TZY4Tn9_phI/AAAAAAAAA14/4b1TWsddQjs/s1600/x2.jpg"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 180px; height: 233px;" src="http://2.bp.blogspot.com/-SUVbwzPrsAk/TZY4Tn9_phI/AAAAAAAAA14/4b1TWsddQjs/s400/x2.jpg" alt="" id="BLOGGER_PHOTO_ID_5590717897224726034" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;To make this happen, you first need to set a presentation variable with the dashboard prompt. In this screen shot, the variable's name is "Y".&lt;br /&gt;&lt;br /&gt;&lt;a href="http://4.bp.blogspot.com/-K3iSbXCcH7o/TZY4suydVZI/AAAAAAAAA2A/piAQ7xpqboE/s1600/x3.jpg"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 38px;" src="http://4.bp.blogspot.com/-K3iSbXCcH7o/TZY4suydVZI/AAAAAAAAA2A/piAQ7xpqboE/s400/x3.jpg" alt="" id="BLOGGER_PHOTO_ID_5590718328552117650" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The trickier part is constructing the filter in the query. It is going to constrain the values between two values, i.e. between Y and Y-3. In the filter, this will be between a SQL Expression (using the variable Y) and the variable Y itself. The screen shot shows how this was entered.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://2.bp.blogspot.com/-ZzJKvuGDWoM/TZY5eKax8JI/AAAAAAAAA2I/i47zz40Pebs/s1600/x5.jpg"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 261px; height: 137px;" src="http://2.bp.blogspot.com/-ZzJKvuGDWoM/TZY5eKax8JI/AAAAAAAAA2I/i47zz40Pebs/s400/x5.jpg" alt="" id="BLOGGER_PHOTO_ID_5590719177782587538" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;On the Criteria Tab, this filter will look like this.&lt;br /&gt;&lt;a href="http://1.bp.blogspot.com/-L7o0w2zKvoQ/TZY51hZqqVI/AAAAAAAAA2Q/mxEcXOM9ETM/s1600/x4.jpg"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 232px; height: 124px;" src="http://1.bp.blogspot.com/-L7o0w2zKvoQ/TZY51hZqqVI/AAAAAAAAA2Q/mxEcXOM9ETM/s400/x4.jpg" alt="" id="BLOGGER_PHOTO_ID_5590719579088922962" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Just one other thing is required: you have to click Protect Filter or it will be overwritten by the value you enter in the dashboard prompt.&lt;br /&gt;&lt;a href="http://2.bp.blogspot.com/-tLZOWvQntuI/TZY67a_wUYI/AAAAAAAAA2Y/1fWv75i6M_w/s1600/x6.jpg"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 346px; height: 147px;" src="http://2.bp.blogspot.com/-tLZOWvQntuI/TZY67a_wUYI/AAAAAAAAA2Y/1fWv75i6M_w/s400/x6.jpg" alt="" id="BLOGGER_PHOTO_ID_5590720779960471938" border="0" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1481199104475905802-2696473186536186526?l=kpipartners.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/2696473186536186526'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/2696473186536186526'/><link rel='alternate' type='text/html' href='http://kpipartners.blogspot.com/2011/04/selecting-single-value-and-showing.html' title='Selecting a Single Value and Showing a Range'/><author><name>Kurt Wolff</name><uri>http://www.blogger.com/profile/07922051559586431987</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><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-shJxmL7guc8/TZY4G2Bv8vI/AAAAAAAAA1w/VCrW9J-dea8/s72-c/x1.jpg' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-1481199104475905802.post-2367444048559469804</id><published>2010-12-30T19:06:00.001-08:00</published><updated>2010-12-30T20:24:39.756-08:00</updated><title type='text'>fmap</title><content type='html'>The OBIEE Answers (10g) user interface says to use "fmap" when referring to resources on the presentation server.&lt;span xmlns=""&gt;&lt;p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_vx0ImXMybTU/TR1TvxhbGKI/AAAAAAAAA0E/3aEYM8Yfkjc/s1600/fmap1.png"&gt;&lt;img style="cursor: pointer; width: 400px; height: 211px;" src="http://2.bp.blogspot.com/_vx0ImXMybTU/TR1TvxhbGKI/AAAAAAAAA0E/3aEYM8Yfkjc/s400/fmap1.png" alt="" id="BLOGGER_PHOTO_ID_5556689595457542306" border="0" /&gt;&lt;/a&gt;   &lt;/p&gt;&lt;p&gt;Many have wondered: what does "fmap" refer to and how do you use it? If it's a "relative path", what's it relative to? If it's relative to the root directory of the presentation, is it necessary?&lt;br /&gt;&lt;/p&gt;&lt;p&gt;As far as I know (with the help of many Google searches on the topic), "fmap" doesn't seem to be a widely used construct. I may not have looked thoroughly enough, but outside of OBIEE, it doesn't seem as though fmap has a meaning.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Questions about fmap come up quite frequently in OBIEE user forums. Quite a few OBIEE bloggers have written about it, but it's interesting to note that sometimes they disagree.&lt;br /&gt;&lt;/p&gt;&lt;pre&gt;&lt;span style="font-size:130%;"&gt;&lt;code&gt;&lt;span style="font-family:Times New Roman;"&gt;For example, Gerard Nico&lt;/span&gt;&lt;br /&gt;(&lt;a href="http://gerardnico.com/wiki/dat/obiee/fmap"&gt;&lt;span style="text-decoration: underline;color:blue;" &gt;http://gerardnico.com/wiki/dat/obiee/fmap&lt;/span&gt;&lt;/a&gt;) &lt;span style="font-family:Times New Roman;"&gt;says that fmap is equivalent&lt;br /&gt;to the path&lt;/span&gt; OracleBIData_Home\web\res\yourskin. &lt;span style="font-family:Times New Roman;"&gt;(Presumably, given the back&lt;br /&gt;slashes in his path statement, he's writing about a presentation server running on Windows.)&lt;br /&gt;&lt;/span&gt;&lt;/code&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span style="font-size:100%;"&gt;&lt;code&gt;&lt;span style=";font-family:Times New Roman;font-size:130%;"  &gt;Venkatakrishnan J's blog entry&lt;br /&gt;(&lt;a href="http://oraclebizint.wordpress.com/2008/01/22/oracle-bi-ee-101332-help-url-in-title-view"&gt;&lt;span style="text-decoration: underline;color:blue;" &gt;http://oraclebizint.wordpress.com/2008/01/22/oracle-bi-ee-101332-help-url-in-title-view&lt;/span&gt;&lt;/a&gt;/ )&lt;br /&gt;says, on the other hand, that fmap is equivalent to the path &lt;/span&gt;&lt;span style="font-size:130%;"&gt;&lt;br /&gt;{OracleBI}\oc4j_bi\j2ee\home\applications\analytics\analytics\Missing_&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family:Times New Roman;"&gt;&lt;span style="font-size:130%;"&gt;and advises creating the directory Missing_ to bring the server structure into alignment with fmap. (Presumably,&lt;br /&gt;Venkat is writing about a Linux environment).&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/code&gt;&lt;/span&gt;&lt;/pre&gt;&lt;p&gt;&lt;span style=";font-family:Times New Roman;font-size:100%;"  &gt;Both Gerard and Venkat are OBIEE experts. So the difference is worth noting. One of the respondents to Venkat's blog &lt;/span&gt;&lt;span style="font-size:100%;"&gt;added&lt;/span&gt;&lt;span style=";font-family:Times New Roman;font-size:100%;"  &gt; that in a Windows server running IIS the Missing_ folder needed to be created off the default IIS directory.  Jason, anoth&lt;/span&gt;&lt;span style="font-size:100%;"&gt;er respondent to Venkat, advised&lt;/span&gt;&lt;span style=";font-family:Times New Roman;font-size:100%;"  &gt; that to get rid of the need for the Missing_ folder, &lt;/span&gt;&lt;span style="font-size:100%;"&gt;add "/../"making the path go back to the root. &lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:100%;"&gt;These two blog entries focused on the logo or help files referenced in the Title view. However, there are other places where you might like to reference an image or file, such as in a dashboard "Link or Image" object or in a conditional format. The next screenshot is from the conditional format dialog where you assign an image.&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;/span&gt;&lt;span xmlns=""&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;&lt;a href="http://2.bp.blogspot.com/_vx0ImXMybTU/TR1UTPrecwI/AAAAAAAAA0M/McUA0WSUzRI/s1600/fmap2.png"&gt;&lt;img style="cursor: pointer; width: 400px; height: 84px;" src="http://2.bp.blogspot.com/_vx0ImXMybTU/TR1UTPrecwI/AAAAAAAAA0M/McUA0WSUzRI/s400/fmap2.png" alt="" id="BLOGGER_PHOTO_ID_5556690204848190210" border="0" /&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;br /&gt;&lt;br /&gt;Another place you might want to use it in a column formula.&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:100%;"&gt;This blog post will explore each of these use cases. But first, a word about the differences between Windows and Linux servers.&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;h1&gt;&lt;span style="font-size:100%;"&gt;Using fmap on a Windows Server&lt;br /&gt;&lt;/span&gt;&lt;/h1&gt;&lt;p&gt;&lt;span style="font-size:100%;"&gt;As a first step to understand "fmap", determine what the "root" directory of the web server is. There are a couple of ways to do this.&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:100%;"&gt;For the first test, I used a 10g OBIEE server running on Windows XP with oc4j. I logged on to this server using the following URL:&lt;br /&gt;&lt;a href="http://&amp;amp;%7e%7especial_remove%21/#%7E%7Elt;server&amp;amp;%7E%7ESPECIAL_REMOVE%21#%7E%7Egt;/analytics/saw.dll?Answers"&gt;http://server/analytics/saw.dll?Answers&lt;/a&gt;&lt;/span&gt;    &lt;span style="font-size:100%;"&gt;&lt;br /&gt;&lt;/span&gt;   &lt;/p&gt;&lt;p&gt;&lt;span style="font-size:100%;"&gt;Since the first part of this URL (through "/analytics") takes you to the root directory of the presentation server, I searched the server to find where saw.dll was located.&lt;br /&gt;I found it in the folder C:\OracleBI\web\app. So that looked like the root directory of the OBIEE presentation server.&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:100%;"&gt;&lt;strong&gt;Test 1 – Using the Image File Name Alone&lt;br /&gt;&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:100%;"&gt;For a test image, I wanted something that would not by chance be an image in one of the directories set up by OBIEE. I used a copy of the KPI Partners logo , naming it test.gif.&lt;br /&gt;I copied it to the presumed root directory, C:\OracleBI\web\app.&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:100%;"&gt;Then I constructed a query and entered the image file name in the Help URL edit box in the Title view.&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;/span&gt;&lt;span xmlns=""&gt;&lt;p&gt;&lt;a href="http://1.bp.blogspot.com/_vx0ImXMybTU/TR1UtG4VPBI/AAAAAAAAA0U/hLC8HbFWFR4/s1600/fmap3.png"&gt;&lt;img style="cursor: pointer; width: 385px; height: 288px;" src="http://1.bp.blogspot.com/_vx0ImXMybTU/TR1UtG4VPBI/AAAAAAAAA0U/hLC8HbFWFR4/s400/fmap3.png" alt="" id="BLOGGER_PHOTO_ID_5556690649162791954" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;span style="font-size:100%;"&gt;When I passed the mouse over the  "&lt;strong&gt;?"  &lt;/strong&gt;icon in the Title view, the browser displayed the path at the bottom of the window:&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:100%;"&gt;&lt;a href="http://&amp;amp;%7e%7especial_remove%21/#%7E%7Elt;server%20&amp;amp;%7E%7ESPECIAL_REMOVE%21#%7E%7Egt;/analytics/test.gif"&gt;http://server/analytics/test.gif&lt;/a&gt; .&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:100%;"&gt;I clicked on the question mark icon in the title view, and the browser displayed the test image.  It was not necessary to re-start any services.&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:100%;"&gt;&lt;strong&gt;Test 2 – Using fmap&lt;br /&gt;&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:100%;"&gt;For the second test, I added "fmap:/" when writing the Help URL.&lt;/span&gt;&lt;span&gt;&lt;span xmlns=""&gt;&lt;a href="http://1.bp.blogspot.com/_vx0ImXMybTU/TR1V5xyyBsI/AAAAAAAAA0c/x_kqnB8SaKI/s1600/fmap4.png"&gt;&lt;img style="cursor: pointer; width: 285px; height: 32px;" src="http://1.bp.blogspot.com/_vx0ImXMybTU/TR1V5xyyBsI/AAAAAAAAA0c/x_kqnB8SaKI/s400/fmap4.png" alt="" id="BLOGGER_PHOTO_ID_5556691966352295618" border="0" /&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;/span&gt;&lt;span xmlns=""&gt;&lt;p&gt;&lt;span style="font-size:100%;"&gt;Now when the mouse hovered on the question mark icon, the browser showed a different path at the bottom of the window&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:100%;"&gt;&lt;a href="http://&amp;amp;%7e%7especial_remove%21/#%7E%7Elt;server&amp;amp;%7E%7ESPECIAL_REMOVE%21#%7E%7Egt;/analytics/Missing_/test.gif"&gt;http://server/analytics/Missing_/test.gif&lt;/a&gt;&lt;br /&gt;&lt;/span&gt;   &lt;/p&gt;&lt;p&gt;&lt;span style="font-size:100%;"&gt;I created a new directory named "Missing_" below the previous path:&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:100%;"&gt;C:\OracleBI\web\app\Missing_&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:100%;"&gt;With a copy ( "test3.gif") of test.gif in this directory I entered fmap:/test3.gif in the Title view &lt;em&gt;Help URL&lt;/em&gt; edit box and clicked on the question icon. The browser displayed the test3.gif image.&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:100%;"&gt;&lt;strong&gt;Further Testing With Other Use Cases&lt;br /&gt;&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:100%;"&gt;I did test with the file name alone as well as with fmap:/filename and fmap:/../filename constructs.&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:100%;"&gt;The &lt;strong&gt;logo in the Title view&lt;/strong&gt; behaved the same way as the Help URL. So did the dashboard's "&lt;strong&gt;Link or Image&lt;/strong&gt;" objects.&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:100%;"&gt;The &lt;strong&gt;formula in Answers&lt;/strong&gt;, however, did not. With the data format of the column set as Image URL, having the file name in the formula would produce an image in the results.&lt;br /&gt;However, formulas using fmap did not.&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:100%;"&gt;When the file name and fmap paths were used in &lt;strong&gt;conditional formats&lt;/strong&gt;, everything worked, although with some differences.&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:100%;"&gt;Here is a screen shot, with three different versions of the Title view along with the table view. The table view shows results with the three different formulas in the first three columns,&lt;br /&gt;and three different conditional format formulas in the last three columns.&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;/span&gt;&lt;span xmlns=""&gt;&lt;h1&gt;&lt;a href="http://4.bp.blogspot.com/_vx0ImXMybTU/TR1Wh-XdqPI/AAAAAAAAA0k/Hdp2FtqlZe0/s1600/fmap5.png"&gt;&lt;img style="cursor: pointer; width: 400px; height: 130px;" src="http://4.bp.blogspot.com/_vx0ImXMybTU/TR1Wh-XdqPI/AAAAAAAAA0k/Hdp2FtqlZe0/s400/fmap5.png" alt="" id="BLOGGER_PHOTO_ID_5556692656922142962" border="0" /&gt;&lt;/a&gt;&lt;/h1&gt;&lt;h1&gt;&lt;span style="font-size:100%;"&gt;Using fmap on a Linux Server&lt;br /&gt;&lt;/span&gt;&lt;/h1&gt;&lt;p&gt;&lt;span style="font-size:100%;"&gt;I used a Linux server running oc4j to do additional testing.&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:100%;"&gt;I determined the root directory to be I copied test.gif to be /u01/app/oracle/product/j2ee/j2ee/home/applications/analytics/analytics . I put a copy of test.gif in this folder and a copy of test3.gif to a new Missing_ folder under this path – i.e. /u01/app/oracle/product/j2ee/j2ee/home/applications/analytics/analytics/Missing_.&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:100%;"&gt;&lt;strong&gt;Title View: &lt;/strong&gt;The title view behaved the same way as it did on Windows .&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style="text-align: justify;"&gt;&lt;span style="font-size:100%;"&gt;&lt;strong&gt;Formulas&lt;/strong&gt;: The column formulas using fmap failed to produce an image, just as on Windows. &lt;strong&gt;Conditional formatting&lt;/strong&gt;: The conditional formatting using just the filename failed to render an image in Linux, while conditional formatting using fmap did.&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;/span&gt;&lt;span xmlns=""&gt;&lt;p&gt;&lt;a href="http://1.bp.blogspot.com/_vx0ImXMybTU/TR1XAp7HXAI/AAAAAAAAA0s/eorSRbSkR9k/s1600/fmap6.png"&gt;&lt;img style="cursor: pointer; width: 400px; height: 139px;" src="http://1.bp.blogspot.com/_vx0ImXMybTU/TR1XAp7HXAI/AAAAAAAAA0s/eorSRbSkR9k/s400/fmap6.png" alt="" id="BLOGGER_PHOTO_ID_5556693184010476546" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:100%;"&gt;&lt;strong&gt;Conditional Format Dialog Boxes&lt;br /&gt;&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:100%;"&gt;Interestingly, the only case where conditional formatting dialog boxes did what you think they should do was the case that did not, in the end, produce the correct conditional format!  Note these screen shots for conditional formatting using just test.gif (no fmap:/).&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;&lt;a href="http://1.bp.blogspot.com/_vx0ImXMybTU/TR1X32I6aLI/AAAAAAAAA00/d6wLnpardDw/s1600/fmap7.png"&gt;&lt;img style="cursor: pointer; width: 400px; height: 102px;" src="http://1.bp.blogspot.com/_vx0ImXMybTU/TR1X32I6aLI/AAAAAAAAA00/d6wLnpardDw/s400/fmap7.png" alt="" id="BLOGGER_PHOTO_ID_5556694132182378674" border="0" /&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;/span&gt;&lt;span xmlns=""&gt;&lt;p&gt;&lt;a href="http://4.bp.blogspot.com/_vx0ImXMybTU/TR1X4Kr0UbI/AAAAAAAAA08/aHcV5Rh0sAs/s1600/fmap8.png"&gt;&lt;img style="cursor: pointer; width: 400px; height: 201px;" src="http://4.bp.blogspot.com/_vx0ImXMybTU/TR1X4Kr0UbI/AAAAAAAAA08/aHcV5Rh0sAs/s400/fmap8.png" alt="" id="BLOGGER_PHOTO_ID_5556694137697489330" border="0" /&gt;&lt;/a&gt;   &lt;/p&gt;&lt;/span&gt;&lt;span xmlns=""&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;&lt;a href="http://3.bp.blogspot.com/_vx0ImXMybTU/TR1X4tsBNGI/AAAAAAAAA1E/ueVG7lCyrfE/s1600/fmap9.png"&gt;&lt;img style="cursor: pointer; width: 400px; height: 64px;" src="http://3.bp.blogspot.com/_vx0ImXMybTU/TR1X4tsBNGI/AAAAAAAAA1E/ueVG7lCyrfE/s400/fmap9.png" alt="" id="BLOGGER_PHOTO_ID_5556694147093574754" border="0" /&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:100%;"&gt;The dialogs look good, but it doesn't work!&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:100%;"&gt;Compare these to the screen shots when reviewing the setup for conditional formatting using either of the fmap constructs&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;/span&gt;&lt;span xmlns=""&gt;&lt;p&gt;&lt;a href="http://3.bp.blogspot.com/_vx0ImXMybTU/TR1YpL8TJmI/AAAAAAAAA1M/5I9B-KQAJU0/s1600/fmap10.png"&gt;&lt;img style="cursor: pointer; width: 400px; height: 204px;" src="http://3.bp.blogspot.com/_vx0ImXMybTU/TR1YpL8TJmI/AAAAAAAAA1M/5I9B-KQAJU0/s400/fmap10.png" alt="" id="BLOGGER_PHOTO_ID_5556694979848644194" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;/span&gt;&lt;span xmlns=""&gt;&lt;p&gt;&lt;span&gt;&lt;span&gt;&lt;a href="http://2.bp.blogspot.com/_vx0ImXMybTU/TR1Ypq8cxLI/AAAAAAAAA1U/a4Qr9quoOcU/s1600/fmap11.png"&gt;&lt;img style="cursor: pointer; width: 400px; height: 64px;" src="http://2.bp.blogspot.com/_vx0ImXMybTU/TR1Ypq8cxLI/AAAAAAAAA1U/a4Qr9quoOcU/s400/fmap11.png" alt="" id="BLOGGER_PHOTO_ID_5556694988170773682" border="0" /&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;If you go back into these dialogs to change anything, you find that the buttons are not functional. For example,  clicking on the Image button in the first dialog does nothing. When the dialog boxes are rendered incorrectly, the conditional formatting actually works, and vice-versa.&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:100%;"&gt;&lt;strong&gt;Link or Image Objects&lt;br /&gt;&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:100%;"&gt;On dashboard Link or Image objects, all three variations work identically. Here's a screen shot from the dashboard with three different Link or Image objects.&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;/span&gt;&lt;span xmlns=""&gt;&lt;h1&gt;&lt;a href="http://3.bp.blogspot.com/_vx0ImXMybTU/TR1ZF4pwIXI/AAAAAAAAA1c/Jcxuj8NZQto/s1600/fmap12.png"&gt;&lt;img style="cursor: pointer; width: 262px; height: 188px;" src="http://3.bp.blogspot.com/_vx0ImXMybTU/TR1ZF4pwIXI/AAAAAAAAA1c/Jcxuj8NZQto/s400/fmap12.png" alt="" id="BLOGGER_PHOTO_ID_5556695472886784370" border="0" /&gt;&lt;/a&gt;&lt;/h1&gt;&lt;h1&gt;&lt;span style="font-size:100%;"&gt;Version Differences?&lt;br /&gt;&lt;/span&gt;&lt;/h1&gt;&lt;p&gt;&lt;span style="font-size:100%;"&gt;As a double check, I took the same query and ran it on a different Linux server. Whether the difference in versions was the significant factor or something else (perhaps some variation during the install), on the second Linux server I saw different results. On the second Linux server, all three forms of conditional formatting worked.&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;/span&gt;&lt;span xmlns=""&gt;&lt;p&gt;&lt;a href="http://1.bp.blogspot.com/_vx0ImXMybTU/TR1ZXk47ZOI/AAAAAAAAA1k/cFXdm2mPABQ/s1600/fmap13.png"&gt;&lt;img style="cursor: pointer; width: 400px; height: 140px;" src="http://1.bp.blogspot.com/_vx0ImXMybTU/TR1ZXk47ZOI/AAAAAAAAA1k/cFXdm2mPABQ/s400/fmap13.png" alt="" id="BLOGGER_PHOTO_ID_5556695776819373282" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;&lt;span style="font-size:100%;"&gt;&lt;strong&gt;You Can't Use fmap to Climb the Tree&lt;br /&gt;&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:100%;"&gt;As I mentioned,  fmap:&lt;strong&gt;/.. /&lt;/strong&gt;  indicates the parent directory of the fmap directory (i.e. the parent directory of the Missing_directory. On our server the path being referenced by fmap was  /u01/app/oracle/product/j2ee/j2ee/home/applications/analytics/analytics/Missing_). If fmap:/football.jpg results in the URL &lt;a href="http://demotpi01:7777/analytics/Missing_/football.jpg"&gt;http://server/analytics/Missing_/football.jpg&lt;/a&gt;,&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:100%;"&gt;and fmap:/../football.jpg results in the URL&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:100%;"&gt;&lt;a href="http://demotpi01:7777/analytics/football.jpg"&gt;http://server/analytics/football.jpg&lt;/a&gt;, what does fmap:/../../baseball.jpg result in?&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:100%;"&gt;In other words if fmap:/football.jpg references the file &lt;/span&gt;&lt;span style=";font-family:Times New Roman;font-size:100%;"  &gt;/u01/app/oracle/product/j2ee/j2ee/home/applications/analytics/analytics/Missing_/football.jpg&lt;/span&gt;&lt;span style="font-size:100%;"&gt;,&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:100%;"&gt;and fmap:/../football.jpg references the file&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=";font-family:Times New Roman;font-size:100%;"  &gt;/u01/app/oracle/product/j2ee/j2ee/home/applications/analytics/analytics/football.jpg,&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:100%;"&gt;what happens when you use fmap:/../../baseball.jpg?&lt;/span&gt;&lt;span style=";font-family:Times New Roman;font-size:100%;"  &gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:100%;"&gt;Well, it results in a link &lt;a href="http://demotpi01:7777/baseball.jpg"&gt;http://server/baseball.jpg&lt;/a&gt; . This represents a reference to the file&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=";font-family:Times New Roman;font-size:100%;"  &gt;/u01/app/oracle/product/j2ee/j2ee/home/applications/analytics//baseball.jpg&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;br /&gt;&lt;/span&gt;   &lt;/p&gt;&lt;p&gt;&lt;span style="font-size:100%;"&gt;This link will not work, even if the reference is correct.&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:100%;"&gt;In other words, once the path is above the analytics directory (i.e. above fmap:/../), references fail to work. You can't use fmap to climb the tree above the analytics directory.&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:100%;"&gt;&lt;strong&gt;Conclusion&lt;br /&gt;&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:100%;"&gt;Can you keep all this straight? I confess that I can't. That's why I wrote it down here – for my reference as much as anyone's! Why the inconsistent behaviors exist is beyond me. I don't know if anything could have been done in the OBIEE product design to bring this all together with some degree of consistency or not. (Certainly the documentation could have been more helpful.) Maybe it's a case where different programmers working on different sections of the code did different things, and no one ever cared enough (no sales were lost because of it) to fix it. Or maybe it's best chalked up as a fact of life that just happens when you work in world of browsers, servers, and the Internet.&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:100%;"&gt;It would probably be a good idea to do tests like these on your Presentation Servers and find out what works and what doesn't.&lt;/span&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1481199104475905802-2367444048559469804?l=kpipartners.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/2367444048559469804'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/2367444048559469804'/><link rel='alternate' type='text/html' href='http://kpipartners.blogspot.com/2010/12/that-confusing-fmap-thing.html' title='fmap'/><author><name>Kurt Wolff</name><uri>http://www.blogger.com/profile/07922051559586431987</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><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_vx0ImXMybTU/TR1TvxhbGKI/AAAAAAAAA0E/3aEYM8Yfkjc/s72-c/fmap1.png' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-1481199104475905802.post-2504307299807977283</id><published>2010-12-29T08:19:00.000-08:00</published><updated>2010-12-29T08:32:44.333-08:00</updated><title type='text'>Creating an Alias vs. Duplicating a Physical Table</title><content type='html'>&lt;div&gt;When you duplicate a table, you create a new physical table with a new name. If this table is involved in a query, the SQL FROM clause will list this table. If the table does not exist in the database, then an error will occur.&lt;br /&gt;&lt;br /&gt;Creating an alias creates a copy of the metadata table object that will be referenced in SQL with a new alias name. The alias name in SQL, as it is for all tables, will be derived its metadata ID.&lt;br /&gt;&lt;br /&gt;To see the table IDs in metadata, use the Query Repository utility. Here are some physical tables (and aliases) in a repository that I’ve created. It’s the last five digits of the ID that will be used to create the table aliases in SQL.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://2.bp.blogspot.com/_vx0ImXMybTU/TRtiaP8EL0I/AAAAAAAAAyU/v71axHvKAQ8/s1600/query_rpd_for_tables.jpg"&gt;&lt;img style="WIDTH: 320px; HEIGHT: 43px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5556142768386223938" border="0" alt="" src="http://2.bp.blogspot.com/_vx0ImXMybTU/TRtiaP8EL0I/AAAAAAAAAyU/v71axHvKAQ8/s320/query_rpd_for_tables.jpg" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;PRODUCT_10 is a table that will referenced in a SQL FROM clause as PRODUCT_10 and will be given the alias T25248.&lt;br /&gt;&lt;br /&gt;PRODUCT_119 is another physical table in the database, identical in structure to PRODUCT_10. It might have been created by duplicating the metadata table PRODUCT_10 and then renaming the duplicate as PRODUCT_119. It will be referenced in the SQL FROM clause as PRODUCT_119 with the alias T23388.&lt;br /&gt;&lt;br /&gt;The metadata table Product_Retailer is an alias of PRODUCT_10. It has its own metadata ID, 3001:46690, and will be given the alias of T46690.&lt;br /&gt;&lt;br /&gt;SQL using both the original table, PRODUCT_10, and its alias, Product_Retailer, will look like this, with the metadata alias name included (optionally, depending on your database features) as a comment:&lt;br /&gt;&lt;br /&gt;FROM&lt;br /&gt;PRODUCT_10 T25248,&lt;br /&gt;PRODUCT_10 T46690 /* Product_Retailer */...&lt;br /&gt;&lt;br /&gt;If you duplicate a table in metadata, then that new table (with its new name) must map to a table in the physical database that has that name. If it doesn't exist, the SQL issued will generate an error.&lt;br /&gt;&lt;br /&gt;An alias is a reference to a table that already exists, not a separate database object.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1481199104475905802-2504307299807977283?l=kpipartners.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/2504307299807977283'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/2504307299807977283'/><link rel='alternate' type='text/html' href='http://kpipartners.blogspot.com/2010/12/creating-alias-vs-duplicating-physical.html' title='Creating an Alias vs. Duplicating a Physical Table'/><author><name>Kurt Wolff</name><uri>http://www.blogger.com/profile/07922051559586431987</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><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_vx0ImXMybTU/TRtiaP8EL0I/AAAAAAAAAyU/v71axHvKAQ8/s72-c/query_rpd_for_tables.jpg' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-1481199104475905802.post-5531631772743463671</id><published>2010-08-02T12:33:00.000-07:00</published><updated>2010-08-02T12:43:27.727-07:00</updated><title type='text'>Using Session Variables in Select Tables in the Physical Layer</title><content type='html'>There are many times when it is very beneficial to pass the value of  session variables (or report variables)  into the SQL used to define a Select table in the physical layer. This allows the select statement to focus on just the data you want, rather than creating a view with potentially millions of rows and then subsequently applying a filter to that result set.&lt;br /&gt;&lt;br /&gt;There are three cases to consider, depending on whether the session variable is intended to filter a column that has a numeric, varchar, or date data type.&lt;br /&gt;&lt;br /&gt;The first case is where a session variable has a numeric value. In the following example, the session variable RETAILERID has been assigned a numeric value. The intent is to filter that data just for that retailer. COMPANYID is the name of a physical column. The syntax is:&lt;br /&gt;&lt;br /&gt;WHERE COMPANYID=ValueOf(NQ_SESSION.RETAILERID)&lt;br /&gt;&lt;br /&gt;The second case is where a session variable needs to be evaluated as a string. In this case, enclose the ValueOf function (including the name of the session variable) in single quotes.&lt;br /&gt;&lt;br /&gt;WHERE upper(SALESREP) = upper('valueof(NQ_SESSION.USER)')&lt;br /&gt;&lt;br /&gt;The third case, dates, is the hardest. Dates are, frankly, inordinately messy in OBIEE. There are a plethora of ways that dates can get formatted depending on which application is being used to select the dates. It would be nice if there was a single place where you could say “I’d like dates to be formatted like this.” But there isn’t (a huge oversight, in my opinion), and if you attempt to descend into the javascript code forest to tweak things – well, good luck. &lt;br /&gt;&lt;br /&gt;The approach I’ve used, which is not ideal but has worked for me, is to hedge your bets in the Select statements. For example, the format of a date report variable can vary, depending on whether the user has changed the default value set by a dashboard calendar prompt.&lt;br /&gt;&lt;br /&gt;For example, here are dates as set by the default values in the prompt.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_vx0ImXMybTU/TFced38BmwI/AAAAAAAAAxc/IIEYW6fdrF4/s1600/defaultdates.png.png"&gt;&lt;img style="float: left; margin: 0pt 10px 10px 0pt; cursor: pointer; width: 273px; height: 53px;" src="http://2.bp.blogspot.com/_vx0ImXMybTU/TFced38BmwI/AAAAAAAAAxc/IIEYW6fdrF4/s320/defaultdates.png.png" alt="" id="BLOGGER_PHOTO_ID_5500898968437103362" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;When the user modifies the date range using the first calendar, the format of the first date changes.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_vx0ImXMybTU/TFce9j12XOI/AAAAAAAAAxk/GLk8y5ar9eM/s1600/changeddates.png.png"&gt;&lt;img style="cursor: pointer; width: 280px; height: 58px;" src="http://2.bp.blogspot.com/_vx0ImXMybTU/TFce9j12XOI/AAAAAAAAAxk/GLk8y5ar9eM/s320/changeddates.png.png" alt="" id="BLOGGER_PHOTO_ID_5500899512798305506" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;If these date prompts are setting report variables, you need to be able to deal with both formats. I’ve done it this way.&lt;br /&gt;&lt;br /&gt;&lt;table.column&gt; BETWEEN case when substr('valueof(NQ_SESSION. StartDate)', 1, 3) = '200' or substr('valueof(NQ_SESSION. StartDate)', 1, 3) = '201' then to_date(substr('valueof(NQ_SESSION.StartDate)',1,10), 'yyyy-mm-dd')&lt;br /&gt;else  to_date('valueof(NQ_SESSION.StartDate)', 'mm/dd/yyyy') end&lt;br /&gt; AND  case when substr('valueof(NQ_SESSION.EndDate)', 1, 3) = '200' or substr('valueof(NQ_SESSION.EndDate)', 1, 3) = '201' then to_date(substr('valueof(NQ_SESSION.EndDate)',1,10), 'yyyy-mm-dd')&lt;br /&gt;else  to_date('valueof(NQ_SESSION.EndDate)', 'mm/dd/yyyy') end&lt;br /&gt;&lt;br /&gt;Note that the substring formulas, which have to span dates from 2000 through 2019, need the comparisons to both ‘200’ and ‘201’. Of course, next decade, the formulas will need further adjusting, but once every 10 years isn’t too bad!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1481199104475905802-5531631772743463671?l=kpipartners.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/5531631772743463671'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/5531631772743463671'/><link rel='alternate' type='text/html' href='http://kpipartners.blogspot.com/2010/08/using-session-variables-in-select.html' title='Using Session Variables in Select Tables in the Physical Layer'/><author><name>Kurt Wolff</name><uri>http://www.blogger.com/profile/07922051559586431987</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><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_vx0ImXMybTU/TFced38BmwI/AAAAAAAAAxc/IIEYW6fdrF4/s72-c/defaultdates.png.png' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-1481199104475905802.post-6632952810152332898</id><published>2010-05-10T13:48:00.001-07:00</published><updated>2010-05-10T14:05:51.725-07:00</updated><title type='text'>Bullet Graphs</title><content type='html'>&lt;div class="Section1"&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;br /&gt;&lt;p class="MsoNormal"&gt;Stephen Few designed the Bullet Graph as a way to display measurements vs. goals or other benchmarks. The screen shot below shows bullet charts in the column “MTD &amp;amp; Proj Comp MAgo” (Month to Date and Projected Expense Compared to Month Ago).&lt;o:p&gt;&lt;/o:p&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;o:p&gt; &lt;/o:p&gt;&lt;/p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_vx0ImXMybTU/S-hzZqdWzjI/AAAAAAAAAwY/w5xO-ynYd4E/s1600/bullet1.png"&gt;&lt;img style="float: left; margin: 0pt 10px 10px 0pt; cursor: pointer; width: 320px; height: 296px;" src="http://3.bp.blogspot.com/_vx0ImXMybTU/S-hzZqdWzjI/AAAAAAAAAwY/w5xO-ynYd4E/s320/bullet1.png" alt="" id="BLOGGER_PHOTO_ID_5469748632172809778" border="0" /&gt;&lt;/a&gt;&lt;p class="MsoNormal"&gt;&lt;o:p&gt; &lt;/o:p&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;br /&gt;&lt;/p&gt;&lt;p class="MsoNormal"&gt;&lt;br /&gt;&lt;/p&gt;&lt;p class="MsoNormal"&gt;&lt;br /&gt;&lt;/p&gt;&lt;p class="MsoNormal"&gt;&lt;br /&gt;&lt;/p&gt;&lt;p class="MsoNormal"&gt;&lt;br /&gt;&lt;/p&gt;&lt;p class="MsoNormal"&gt;&lt;br /&gt;&lt;/p&gt;&lt;p class="MsoNormal"&gt;&lt;br /&gt;&lt;/p&gt;&lt;p class="MsoNormal"&gt;&lt;br /&gt;&lt;/p&gt;&lt;p class="MsoNormal"&gt;&lt;br /&gt;&lt;/p&gt;&lt;p class="MsoNormal"&gt;The black horizontal bars show the current month to date expense as a percentage of the previous month’s expense amount. The gray horizontal bars show the projected expenses for the current month, assuming linearity. The black vertical bar, which is set at 100%, represents the level of expense in the previous month. &lt;o:p&gt;&lt;/o:p&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;o:p&gt; &lt;/o:p&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;These bullet charts are done using the google charting api.  &lt;o:p&gt;&lt;br /&gt;&lt;/o:p&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;To generate these bullet charts OBIEE needs to generate a url that contains the right parameters. Other web sites have done a good job documenting the parameters of the url. For example, see &lt;a href="http://dealerdiagnostics.com/blog/2008/05/create-bullet-graphs-with-google-charts-in-7-easy-steps/"&gt;http://dealerdiagnostics.com/blog/2008/05/create-bullet-graphs-with-google-charts-in-7-easy-steps/&lt;/a&gt; .&lt;o:p&gt;&lt;br /&gt;&lt;/o:p&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;In this case, the url includes several parameters, each separated by an ampersand, and breaks down like this:&lt;br /&gt;&lt;br /&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;'http://chart.apis.google.com/chart?       &lt;span style="color: rgb(84, 141, 212);"&gt;Google charting&lt;/span&gt; &lt;o:p&gt;&lt;/o:p&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;chs=150x40                                                   &lt;span style="color: rgb(84, 141, 212);"&gt;size of the chart&lt;/span&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&amp;amp;cht=bhs                                                       &lt;span style="color: rgb(84, 141, 212);"&gt;type of chart, horizontal bar&lt;/span&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&amp;amp;chco=000000                                            &lt;span style="color: rgb(84, 141, 212);"&gt;color of the bar = black&lt;/span&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&amp;amp;chbh=15                                                      &lt;span style="color: rgb(84, 141, 212);"&gt;bar width&lt;/span&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&amp;amp;chm=r,000000,0,0.49,0.51,1                 &lt;span style="color: rgb(84, 141, 212);"&gt;vertical line (thin bar) from 49% to 50%&lt;/span&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;|r,CCCCCC,0,0,'||cast((Facts."Tot Net Charges"*valueof(NQ_SESSION.UBDProjection))/(Facts."Tot Net Charges MAgo"*2) as varchar(4))||'                                                 &lt;span style="color: rgb(84, 141, 212);"&gt;gray horizontal bar (projected amount)&lt;/span&gt; &lt;o:p&gt;&lt;/o:p&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&amp;amp;chd=t:'||cast(round(100*Facts."Tot Net Charges"/(Facts."Tot Net Charges MAgo"*2),0) as varchar(4))||'                                                  &lt;span style="color: rgb(84, 141, 212);"&gt;data as a percent of 2*MAgo&lt;/span&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&amp;amp;chxt=x                                                            &lt;span style="color: rgb(84, 141, 212);"&gt;says to label the x axis&lt;/span&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&amp;amp;chxl=0:|0|50%|100%|150%|200%           &lt;span style="color: rgb(84, 141, 212);"&gt;labels for the x axis&lt;/span&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&amp;amp;chxs=0,000000,9'                                         &lt;span style="color: rgb(84, 141, 212);"&gt;specifies the first label (0), color, and size&lt;/span&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;o:p&gt; &lt;/o:p&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;The whole URL looks like this:&lt;o:p&gt;&lt;/o:p&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;o:p&gt; &lt;/o:p&gt;&lt;/p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_vx0ImXMybTU/S-hz7BZr4AI/AAAAAAAAAwg/_sjQ6nrK2VQ/s1600/bulleturl.png"&gt;&lt;img style="cursor: pointer; width: 401px; height: 171px;" src="http://2.bp.blogspot.com/_vx0ImXMybTU/S-hz7BZr4AI/AAAAAAAAAwg/_sjQ6nrK2VQ/s320/bulleturl.png" alt="" id="BLOGGER_PHOTO_ID_5469749205267111938" border="0" /&gt;&lt;/a&gt;&lt;p class="MsoNormal"&gt;&lt;o:p&gt; &lt;/o:p&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;The only two parts of this that reference data from the query are the parts highlighted in yellow and purple.  &lt;o:p&gt;&lt;br /&gt;&lt;/o:p&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;The yellow formula draws the gray bar (color = #CCCCCC) that starts at 0 and ends at a point represented by the fraction being computed by the yellow formula. The formula multiplies the total net charges for the current month by the Projection session variable (days in month/current day of month), then divides that by 2 times the total net charges last month. Since this is going to be part of a url and has to be concatenated with other text, the cast as varchar is needed.  &lt;o:p&gt;&lt;br /&gt;&lt;/o:p&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;The purple formula is the length of the black bar. Again, that is expressed as a fraction (actually here, a percentage) of 2 times the expenses of the previous month. The cast as varchar is needed here, too.&lt;o:p&gt;&lt;br /&gt;&lt;/o:p&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;It’s a little inconsistent in that the black horizontal bar is represented by a number (0 to 100), while the gray bar starts at 0 and extends to a number that has a value between 0 and 1 (projected expense/(2 * MAgo)). The black “vertical line” is a bar that starts at 0.49 and ends at 0.51.&lt;o:p&gt;&lt;br /&gt;&lt;/o:p&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;The final step is to set the column’s data format to Image URL.&lt;o:p&gt;&lt;/o:p&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_vx0ImXMybTU/S-h0kbTv61I/AAAAAAAAAwo/T6I8K9n29d8/s1600/imageurl.png"&gt;&lt;img style="float: left; margin: 0pt 10px 10px 0pt; cursor: pointer; width: 320px; height: 183px;" src="http://3.bp.blogspot.com/_vx0ImXMybTU/S-h0kbTv61I/AAAAAAAAAwo/T6I8K9n29d8/s320/imageurl.png" alt="" id="BLOGGER_PHOTO_ID_5469749916596169554" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;o:p&gt; &lt;/o:p&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;o:p&gt; &lt;/o:p&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;o:p&gt; &lt;/o:p&gt;&lt;/p&gt;  &lt;p class="MsoNormal"&gt;&lt;span style=";font-family:&amp;quot;;font-size:11pt;"  &gt;&lt;o:p&gt; &lt;/o:p&gt;&lt;/span&gt;&lt;/p&gt;  &lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1481199104475905802-6632952810152332898?l=kpipartners.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/6632952810152332898'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/6632952810152332898'/><link rel='alternate' type='text/html' href='http://kpipartners.blogspot.com/2010/05/bullet-graphs.html' title='Bullet Graphs'/><author><name>Kurt Wolff</name><uri>http://www.blogger.com/profile/07922051559586431987</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><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_vx0ImXMybTU/S-hzZqdWzjI/AAAAAAAAAwY/w5xO-ynYd4E/s72-c/bullet1.png' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-1481199104475905802.post-4267453459334198820</id><published>2010-04-25T16:41:00.001-07:00</published><updated>2010-04-25T16:41:02.835-07:00</updated><title type='text'>Table with Fixed Header and Scrollable Data</title><content type='html'>&lt;h3&gt;&amp;#160;&lt;/h3&gt;  &lt;p&gt;A common question is whether OBIEE can display a table and freeze the header “like you can in Excel”. OBIEE does not offer that feature out of the box, but you can do this (more or less) with the narrative view and the appropriate html.&lt;/p&gt;  &lt;p&gt;First a disclaimer: I am a very poor html programmer. What I’m going to show is something I’ve put together after scouring the web for what looked like useful html examples. If you know more about html than I do (and you probably do!), then you can undoubtedly take this farther and build tables with far more bells and whistles. &lt;/p&gt;  &lt;p&gt;The features missing from the examples this blog entry will show comprise a rather long list: you cannot sort columns by clicking on the headers; you can’t drill or navigate; there are no totals or subtotals; repeating values are not suppressed. Anyone who knows how (or has the time to figure out how) to add these features – well, I look forward to reading your blog entries. &lt;/p&gt;  &lt;p&gt;If you use an approach like the ones this blog post shows, you will be working in the narrative view in OBIEE. This is, frankly, an annoying place to have to work. The edit boxes are tiny (especially the prefix edit box). You cannot use Firefox to create the narrative view (for some reason, Firefox will not display, nor let you copy, the entire text of the html in this view). So you end up working in IE and notepad and doing a lot of cutting and pasting. Finally, whatever you create may work in Firefox but not in IE or vice-versa.&lt;/p&gt;  &lt;p&gt;OK, I think all the disclaimers are out of the way, except to say that I should give credit to where the original code examples came from, but, alas, I didn’t write down the original URL for the first example and, besides, I’ve probably butchered it up pretty thoroughly by now. &lt;/p&gt;  &lt;p&gt;The two screen shots represent how the data looks as you move the vertical scroll bar on the right. &lt;b&gt;This one works only in Firefox.&lt;/b&gt; In IE, all the data scrolls, including the column headers.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/S9TS88vrV2I/AAAAAAAAAvo/Ih99_MrTCrg/s1600-h/clip_image002%5B4%5D.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="clip_image002" border="0" alt="clip_image002" src="http://lh5.ggpht.com/_vx0ImXMybTU/S9TS9jECxXI/AAAAAAAAAvs/CXJe7JF7avQ/clip_image002_thumb%5B1%5D.jpg?imgmax=800" width="356" height="335" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/S9TS-MzcwbI/AAAAAAAAAv0/MXZpfVQ3c4k/s1600-h/clip_image004%5B5%5D.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="clip_image004" border="0" alt="clip_image004" src="http://lh4.ggpht.com/_vx0ImXMybTU/S9TS_G2PI-I/AAAAAAAAAv4/1hx4uNX_AGE/clip_image004_thumb%5B2%5D.jpg?imgmax=800" width="356" height="340" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Here’s the code for this, starting with the Prefix section of the narrative view. I won’t say very much about the html here, but one of the nice things about this example is that you can control the alignment of the data. (Sorry about the overabundance of spacing here – this just seems to be something that Windows Live won't let me control.)&lt;/p&gt;  &lt;p&gt;----------------------------------------------------------------------------&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;&amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Strict//EN&amp;quot; &amp;quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd&amp;quot;&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;&amp;lt;html&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;&amp;lt;head&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;&amp;lt;meta http-equiv=&amp;quot;content-type&amp;quot; content=&amp;quot;text/html; charset=iso-8859-1&amp;quot; /&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;&amp;lt;style type='text/css'&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;/* Scrollable Content Height */&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;.scrollContent {&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;height:300px;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;overflow-x:hidden;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;overflow-y:auto;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;}&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;.scrollContent tr {&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;height: auto;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;white-space: nowrap;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;}&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;/* Prevent Mozilla scrollbar from hiding right-most cell content */&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;.scrollContent tr td:last-child {&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;padding-right: 20px;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;}&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;/* Fixed Header Height */&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;.fixedHeader tr {&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;position: relative;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;height: auto;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;}&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;/* Put border around entire table */&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;div.TableContainer {&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;border: 1px solid #7DA87D;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;}&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;/* Table Header formatting */&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;.headerFormat {&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;background-color: white;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;color: #FFFFFF;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;margin: 0px;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;padding: 0px;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;white-space: nowrap;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;font-family: Helvetica;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;font-size: 12px;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;text-decoration: none;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;font-weight: bold;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;}&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;.headerFormat tr td {&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;border: 1px solid #000000;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;background-color: #FF2F4B;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;}&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;/* Table Body (Scrollable Content) formatting */&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;.bodyFormat tr td {&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;color: #000000;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;margin: 0px;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;padding: 0px;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;border-bottom-style: solid; &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;border-bottom-color: white;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;border-bottom-width: 2px; &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;border-right-style: solid; &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;border-right-color: white;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;border-right-width: 2px; &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;border-left-style: solid; &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;border-left-color: white;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;border-left-width: 1px; &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;border-top-style: solid; &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;border-top-color: white;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;border-top-width: 1px; &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;font-family: Helvetica;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;font-size: 10px;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;}&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;&amp;lt;/style&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;&amp;lt;!--[if IE]&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;&amp;lt;style type=&amp;quot;text/css&amp;quot;&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;/* IE Specific Style addition to constrain table from automatically growing in height */&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;div.TableContainer {&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;height: 400px; &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;overflow-x:hidden;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;overflow-y:auto;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;}&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;&amp;lt;/style&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;&amp;lt;![endif]--&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;&amp;lt;/head&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;&amp;lt;body&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;&amp;lt;br /&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;&amp;lt;br /&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;&amp;lt;table cellpadding=&amp;quot;0&amp;quot; cellspacing=&amp;quot;0&amp;quot; border=&amp;quot;0&amp;quot;&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;div class=&amp;quot;TableContainer&amp;quot;&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;&amp;lt;table class=&amp;quot;scrollTable&amp;quot;&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;&amp;lt;thead class=&amp;quot;fixedHeader headerFormat&amp;quot;&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font color="#ff0000" face="Courier"&gt;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;Date&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Fact One&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Count One&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Fact Two&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;Fact Three&amp;lt;/td&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;&amp;lt;td&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;/thead&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;&amp;lt;tbody class=&amp;quot;scrollContent bodyFormat&amp;quot;&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;------------------------------------------------------------------------------&lt;/p&gt;  &lt;p&gt;The section in red directly above contains the column names. Of course, you would want to modify this to match your query.&lt;/p&gt;  &lt;p&gt;The following code goes in the Narrative section of the Narrative view. Again, of course, you would modify this to match your query.&lt;/p&gt;  &lt;p&gt;&lt;font face="cou"&gt;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;@1&amp;lt;/td&amp;gt; &amp;lt;td align=&amp;quot;right&amp;quot;&amp;gt;@2&amp;lt;/td&amp;gt; &amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;@3&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;right&amp;quot;&amp;gt;@4&amp;lt;/td&amp;gt;&amp;lt;td align=&amp;quot;right&amp;quot;&amp;gt;@5&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/S9TS_i7p6zI/AAAAAAAAAv8/TKUGW47yNms/s1600-h/clip_image006%5B5%5D.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="clip_image006" border="0" alt="clip_image006" src="http://lh5.ggpht.com/_vx0ImXMybTU/S9TTAV4HgxI/AAAAAAAAAwA/z1JwQjLr5lY/clip_image006_thumb%5B2%5D.jpg?imgmax=800" width="443" height="236" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Notice how the data alignment can be specified for each column.&lt;/p&gt;  &lt;p&gt;Finally, the following code goes in the Postfix section:&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;&amp;lt;/tbody&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;&amp;lt;/table&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;/table&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;&amp;lt;/body&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;&amp;lt;/html&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;Here’s another example, using a different pattern of html. This one works in both IE and Firefox. However, I was not able to get the data to align as I could with the previous code. This example comes from &lt;a href="http://www.cssplay.co.uk/menu/tablescroll.html"&gt;http://www.cssplay.co.uk/menu/tablescroll.html&lt;/a&gt;. There are actually two examples there. I’ve shown only one.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/S9TTAwgIfeI/AAAAAAAAAwE/WJbitIQpUDg/s1600-h/clip_image008%5B5%5D.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="clip_image008" border="0" alt="clip_image008" src="http://lh4.ggpht.com/_vx0ImXMybTU/S9TTBh1p-uI/AAAAAAAAAwI/c-G-e0Rg3Kc/clip_image008_thumb%5B2%5D.jpg?imgmax=800" width="421" height="133" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/S9TTCp8kWWI/AAAAAAAAAwM/y64wKCb6Ehw/s1600-h/clip_image010%5B5%5D.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="clip_image010" border="0" alt="clip_image010" src="http://lh4.ggpht.com/_vx0ImXMybTU/S9TTDHyvP_I/AAAAAAAAAwQ/juAau-LZMmw/clip_image010_thumb%5B2%5D.jpg?imgmax=800" width="430" height="130" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Prefix:&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;&amp;lt;!DOCTYPE html PUBLIC &amp;quot;-//W3C//DTD XHTML 1.0 Strict//EN&amp;quot; &amp;quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd&amp;quot;&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;&amp;lt;html xmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot; xml:lang=&amp;quot;en&amp;quot;&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;&amp;lt;head&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;&amp;lt;meta http-equiv=&amp;quot;X-UA-Compatible&amp;quot; content=&amp;quot;IE=EmulateIE7&amp;quot; /&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;&amp;lt;meta http-equiv=&amp;quot;Content-Type&amp;quot; content=&amp;quot;text/html; charset=UTF-8&amp;quot; /&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;&amp;lt;style type=&amp;quot;text/css&amp;quot;&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;.outer {&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;position:relative;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;padding:4em 0 3em 0;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;width:54em;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;background:#eee;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;margin:0 auto 3em auto;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;}&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;.innera {&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;overflow:auto;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;width:54em;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;height:9.6em;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;background:#eee;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;}&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;.outer thead tr {&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;position:absolute;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;top:1.5em;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;height:1.5em;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;left:0;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;}&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;.outer th, .outer td {&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;width:10em; &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;text-align:center;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;}&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;.outer th {&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;background:#724a10; &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;color:#fff;}&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;.outer .dk {background:#fff;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;}&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;.tableone {width:650px; border-collapse:collapse; margin:0 auto;}&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;.tabletwo {width:620px; border-collapse:collapse;}&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;.th1 {width:149px;}&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;.th2 {width:99px;}&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;.th3 {width:99px;}&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;.th4 {width:99px;}&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;.th5 {width:200px;}&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;.td1 {width:149px;}&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;.td2 {width:99px;}&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;.td3 {width:99px;}&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;.td4 {width:99px;}&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;.td5 {width:170px;}&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;.tableone {background:#697210; border:1px solid #fff; color:#fff;}&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;.tableone td {border:1px solid #fff; color:#fff;}&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;.tableone tbody {background:#f0c992; color:#000;}&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;.tableone caption {background:#fff; color:#697210; font-size:1.2em; margin:0 auto;}&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;.tabletwo td {background:#eee; color:#000;}&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;.tableone th, .tabletwo th {text-align:center;}&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;.tabletwo tr.dk td {background:#ddd; color:#000;}&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;.innerb {height:10em; overflow:auto;}&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;&amp;lt;/style&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;&amp;lt;/head&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;&amp;lt;div id=&amp;quot;info&amp;quot;&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;&amp;lt;div class=&amp;quot;outer&amp;quot;&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;&amp;lt;div class=&amp;quot;innera&amp;quot;&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;&amp;lt;table&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;&amp;lt;thead&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;&amp;lt;tr&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;&amp;lt;th&amp;gt;DATE&amp;lt;/th&amp;gt; &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;&amp;lt;th&amp;gt;Fact One&amp;lt;/th&amp;gt; &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;&amp;lt;th&amp;gt;Fact Two&amp;lt;/th&amp;gt; &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;&amp;lt;th&amp;gt;Fact Three&amp;lt;/th&amp;gt; &lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;&amp;lt;th&amp;gt;Fact Four&amp;lt;/th&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;&amp;lt;/tr&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;&amp;lt;/thead&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;&amp;lt;tbody&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;The Narrative section contains this.&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;@1&amp;lt;/td&amp;gt; &amp;lt;td&amp;gt;@2&amp;lt;/td&amp;gt; &amp;lt;td&amp;gt;@3&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;@4&amp;lt;/td&amp;gt;&amp;lt;td &amp;gt;@5&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;The Postfix contains this.&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;&amp;lt;/tbody&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;&amp;lt;/table&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;&amp;lt;/div&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;&amp;lt;/div&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1481199104475905802-4267453459334198820?l=kpipartners.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/4267453459334198820'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/4267453459334198820'/><link rel='alternate' type='text/html' href='http://kpipartners.blogspot.com/2010/04/table-with-fixed-header-and-scrollable.html' title='Table with Fixed Header and Scrollable Data'/><author><name>Kurt Wolff</name><uri>http://www.blogger.com/profile/07922051559586431987</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><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/_vx0ImXMybTU/S9TS9jECxXI/AAAAAAAAAvs/CXJe7JF7avQ/s72-c/clip_image002_thumb%5B1%5D.jpg?imgmax=800' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-1481199104475905802.post-4169470990415251361</id><published>2010-02-25T19:36:00.001-08:00</published><updated>2010-02-25T19:40:36.119-08:00</updated><title type='text'>Complex Row Level Security</title><content type='html'>&lt;p&gt;Row level security (constraining a user’s view of the data to rows which meet pre-defined criteria) is a common requirement. This post will explore this topic, using a simple schema with a single fact table and three dimension tables, built around the theme of retail sales. &lt;/p&gt;  &lt;p&gt;The data model in this schema has dimension tables Weeks, Items and Stores. Users can see all weeks. So security does not have to be concerned with the Weeks table. In the dimension tables there are just three items and three stores. &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/S4dBhgCXxDI/AAAAAAAAAsg/VSZYo0RK8nQ/s1600-h/clip_image002%5B3%5D.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="clip_image002" border="0" alt="clip_image002" src="http://lh5.ggpht.com/_vx0ImXMybTU/S4dBiC-tVrI/AAAAAAAAAsk/8PX5W7QLmYo/clip_image002_thumb.jpg?imgmax=800" width="155" height="124" /&gt;&lt;/a&gt; &lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/S4dBirmE51I/AAAAAAAAAso/uuxYqMROek8/s1600-h/clip_image004%5B3%5D.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="clip_image004" border="0" alt="clip_image004" src="http://lh4.ggpht.com/_vx0ImXMybTU/S4dBiwgNfWI/AAAAAAAAAss/GDTaD-6LXCU/clip_image004_thumb.jpg?imgmax=800" width="161" height="124" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Here is the fact table (partially shown). Store, Item, and Week are foreign keys to the dimension tables. “QS” is the fact (Quantity Sold), which has a SUM aggregation rule. &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/S4dBjEvUPHI/AAAAAAAAAsw/G_A4FHbvlAM/s1600-h/clip_image006%5B3%5D.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="clip_image006" border="0" alt="clip_image006" src="http://lh3.ggpht.com/_vx0ImXMybTU/S4dBjoD1SOI/AAAAAAAAAs0/ifMYAAWEETY/clip_image006_thumb.jpg?imgmax=800" width="96" height="244" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;The Business Model looks like this.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/S4dBkOLnRTI/AAAAAAAAAs4/0ipn3Xudq2w/s1600-h/clip_image008%5B3%5D.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="clip_image008" border="0" alt="clip_image008" src="http://lh4.ggpht.com/_vx0ImXMybTU/S4dBkeA7FlI/AAAAAAAAAs8/nnYS8K_bi1c/clip_image008_thumb.jpg?imgmax=800" width="155" height="134" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;The first requirement is to restrict the data visibility of some users to just certain products in all stores. For example, user A should be able to see Product A in all stores, but not products B and C. The second requirement is to restrict other users to only a subset of products in a subset of stores. For example, user B should be able to see data just for Product B but only in Stores 1 and 2. User C should see only Product C but only in Stores 2 and 3. &lt;/p&gt;  &lt;p&gt;A good way to implement this is to use an initialization block to set session variables using row-wise initialization. A database table, such as the one in this schema called RowWiseVars, contains the data visibility rules for users A, B, and C. When each user logs on, an init block will read the table, creating and populating session variables. &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/S4dBk-7iiJI/AAAAAAAAAtA/OgUoFyBN2e8/s1600-h/clip_image010%5B3%5D.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="clip_image010" border="0" alt="clip_image010" src="http://lh3.ggpht.com/_vx0ImXMybTU/S4dBlCwDNmI/AAAAAAAAAtE/kGUjRueCCeU/clip_image010_thumb.jpg?imgmax=800" width="149" height="244" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;The init block contains the following SQL. It sets the session variables Product and Store (the values in the table’s VAR column) and their respective values (the values in the VALUE column) for each user. With row-wise initialization, the names of the columns are immaterial. The values in the first column in the SQL define and name the session variables. The values in the second column populate the session variables that are being defined.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/S4dBlkFieYI/AAAAAAAAAtI/5idbKzdI5jo/s1600-h/clip_image012%5B3%5D.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="clip_image012" border="0" alt="clip_image012" src="http://lh6.ggpht.com/_vx0ImXMybTU/S4dBmFGq6oI/AAAAAAAAAtM/c0DFYzhJ67g/clip_image012_thumb.jpg?imgmax=800" width="244" height="140" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Notice in the following screen shot that the session variables are not listed in the Edit Data Target dialog of the initialization block. Instead, the Row-Wise initialization radio button is clicked on. &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/S4dBmdU6NzI/AAAAAAAAAtQ/Tb3f4DZ0KSM/s1600-h/clip_image014%5B3%5D.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="clip_image014" border="0" alt="clip_image014" src="http://lh5.ggpht.com/_vx0ImXMybTU/S4dBm71GJrI/AAAAAAAAAtU/7VMFCZda5cI/clip_image014_thumb.jpg?imgmax=800" width="244" height="158" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Now defining an rpd group called “RetailUsers”, we set the filters on the Business Model that will be used for this group. Queries could be dimensional only, fact only, or dimensions with facts. Filters are needed for all three cases.&lt;/p&gt;  &lt;p&gt;The fact table filter will cause a join between the fact table and the Item and Store dimension tables and apply a filter on those tables even when the columns in the dimension tables are not involved in the user’s query. The expression builder does not show the session variables in its Session Variables folder, since they are not defined in the RPD but instead are set by row-wise initialization. The session variable names have to be entered manually, as shown in yellow. &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/S4dBnY57IsI/AAAAAAAAAtY/ObzrKk9GN2A/s1600-h/clip_image016%5B3%5D.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="clip_image016" border="0" alt="clip_image016" src="http://lh6.ggpht.com/_vx0ImXMybTU/S4dBnnx4buI/AAAAAAAAAtc/tuTbUNKtLOw/clip_image016_thumb.jpg?imgmax=800" width="244" height="184" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Filters are also defined on the RETAILITEMS and RETAILSTORES dimension tables. This will filter the values that will be returned to the user when queries are constructed that do not have to involve the logical fact table.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/S4dBoMcloUI/AAAAAAAAAtg/mOgrWyEC8RU/s1600-h/clip_image018%5B3%5D.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="clip_image018" border="0" alt="clip_image018" src="http://lh4.ggpht.com/_vx0ImXMybTU/S4dBoheKTQI/AAAAAAAAAtk/6d4jtDUb1fI/clip_image018_thumb.jpg?imgmax=800" width="244" height="77" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Now when user A logs on, the initialization block runs with the following SQL.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/S4dBo7J8hHI/AAAAAAAAAto/vnTWac2rb0w/s1600-h/clip_image020%5B3%5D.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="clip_image020" border="0" alt="clip_image020" src="http://lh6.ggpht.com/_vx0ImXMybTU/S4dBpWwlL1I/AAAAAAAAAts/rh4pumhPAoo/clip_image020_thumb.jpg?imgmax=800" width="100" height="118" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Which produces these results:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/S4dBpsEyeTI/AAAAAAAAAtw/8ZH66cTzwdM/s1600-h/clip_image022%5B3%5D.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="clip_image022" border="0" alt="clip_image022" src="http://lh3.ggpht.com/_vx0ImXMybTU/S4dBqCT7coI/AAAAAAAAAt0/G1zz571z3ug/clip_image022_thumb.jpg?imgmax=800" width="120" height="117" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;The variable Store is set to Store 1, Store 2, and Store 3. The variable Product is set to Product A. &lt;/p&gt;  &lt;p&gt;When user A queries the Items dimension, he only sees product A.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/S4dBqfwO2pI/AAAAAAAAAt4/Pb5OVJA_-Uc/s1600-h/clip_image024%5B3%5D.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="clip_image024" border="0" alt="clip_image024" src="http://lh4.ggpht.com/_vx0ImXMybTU/S4dBqwSJeqI/AAAAAAAAAt8/BhQR-pc2B-8/clip_image024_thumb.jpg?imgmax=800" width="244" height="149" /&gt;&lt;/a&gt; &lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/S4dBrLB1N1I/AAAAAAAAAuA/JmRFXEpv5f0/s1600-h/clip_image026%5B3%5D.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="clip_image026" border="0" alt="clip_image026" src="http://lh3.ggpht.com/_vx0ImXMybTU/S4dBrozVf1I/AAAAAAAAAuE/5pgvAk4gqvE/clip_image026_thumb.jpg?imgmax=800" width="205" height="132" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;The physical SQL generated is&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;select distinct T2372.ITEMNAME as c1     &lt;br /&gt;from       &lt;br /&gt;RETAILITEMS T2372      &lt;br /&gt;where ( T2372.ITEMNAME = 'Product A' )       &lt;br /&gt;order by c1&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;When user A queries just the fact table (i.e. queries for QS only), the result is the sum of QS for Product A in the three stores, which is 3.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/S4dBrzH2xvI/AAAAAAAAAuI/CCPUMSMEZn0/s1600-h/clip_image028%5B3%5D.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="clip_image028" border="0" alt="clip_image028" src="http://lh5.ggpht.com/_vx0ImXMybTU/S4dBsZYg9FI/AAAAAAAAAuM/-3-e3H_Vtak/clip_image028_thumb.jpg?imgmax=800" width="59" height="63" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;The physical SQL is&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;select sum(T2364.QS) as c1     &lt;br /&gt;from       &lt;br /&gt;RETAILSTORES T2376,      &lt;br /&gt;RETAILITEMS T2372,      &lt;br /&gt;RETAILFACTS T2364      &lt;br /&gt;where ( T2364.ITEM = T2372.ITEM       &lt;br /&gt;and T2364.STORE = T2376.STORE       &lt;br /&gt;and T2372.ITEMNAME = 'Product A'       &lt;br /&gt;and (T2376.STORENAME in       &lt;br /&gt;('Store 1', 'Store 2', 'Store 3')))&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;Note, because this rpd group needs to have data filtered by products and stores using two session variables, it is necessary to list all stores for user A in the RowWiseVars table. Alternatively, if A was limited by product only, we could have defined a separate rpd group and used a single session variable for that group. In that case the RowWiseVars table would not have included store information for user A.&lt;/p&gt;  &lt;p&gt;This meets the requirements when the “legal data” is the intersection of the dimensional values. However, it may be that some requirements are more complex and cannot be accommodated by an intersection of dimension values. For example, suppose user D is allowed to see Product A, but only in Store1 and 2, and Product B, but only in Store 3. To handle this requirement we need to create another security table. Let’s call this table &lt;b&gt;ComplexSecurity&lt;/b&gt;. It contains the list of legal product/store combinations for each user – in this case just User D. &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/S4dBsuuuvJI/AAAAAAAAAuQ/ZyzDbh2UEyw/s1600-h/clip_image030%5B3%5D.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="clip_image030" border="0" alt="clip_image030" src="http://lh4.ggpht.com/_vx0ImXMybTU/S4dBtCfYFrI/AAAAAAAAAuU/21GjpTgTAO0/clip_image030_thumb.jpg?imgmax=800" width="195" height="123" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;To filter the data correctly, the ComplexSecurity table must be joined to the fact table, like this. &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/S4dBtcv9gAI/AAAAAAAAAuY/9GWv3Izb2ZQ/s1600-h/clip_image032%5B3%5D.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="clip_image032" border="0" alt="clip_image032" src="http://lh6.ggpht.com/_vx0ImXMybTU/S4dBt8lfFAI/AAAAAAAAAuc/Bi_3UjgH-u8/clip_image032_thumb.jpg?imgmax=800" width="244" height="32" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/S4dBuDedCyI/AAAAAAAAAug/67-z8fpiXA8/s1600-h/clip_image034%5B3%5D.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="clip_image034" border="0" alt="clip_image034" src="http://lh4.ggpht.com/_vx0ImXMybTU/S4dBugHqeoI/AAAAAAAAAuk/44CdfDoxBZw/clip_image034_thumb.jpg?imgmax=800" width="244" height="149" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;In addition to constraining fact table data, it is also necessary to constrain the values returned by dimensional queries (e.g. when browsing using a dashboard prompt). To do this, we can use the session variable strategy that we used in the previous case and add these rows to the RowWiseVars table.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/S4dBu-QI85I/AAAAAAAAAuo/soZnj1fscaU/s1600-h/clip_image036%5B3%5D.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="clip_image036" border="0" alt="clip_image036" src="http://lh3.ggpht.com/_vx0ImXMybTU/S4dBvHHbL5I/AAAAAAAAAus/eyZo0lkDxDc/clip_image036_thumb.jpg?imgmax=800" width="183" height="185" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;We need to define a new RPD group with the dimensional and fact table filters.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/S4dBvinlHYI/AAAAAAAAAvY/OzES-aEwWVM/s1600-h/clip_image038%5B5%5D.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="clip_image038" border="0" alt="clip_image038" src="http://lh6.ggpht.com/_vx0ImXMybTU/S4dBwHzeaSI/AAAAAAAAAvc/eeOgaKFnXU8/clip_image038_thumb%5B2%5D.jpg?imgmax=800" width="397" height="128" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;When D queries stores alone, he sees all three stores: &lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/S4dBwTJ8lUI/AAAAAAAAAu4/GBVIZQlSo-I/s1600-h/clip_image040%5B3%5D.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="clip_image040" border="0" alt="clip_image040" src="http://lh4.ggpht.com/_vx0ImXMybTU/S4dBwyr1CII/AAAAAAAAAu8/bsoFVmlQvoE/clip_image040_thumb.jpg?imgmax=800" width="80" height="81" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;The physical SQL includes the Store session variable values.&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;select distinct T2376.STORENAME as c1     &lt;br /&gt;from       &lt;br /&gt;RETAILSTORES T2376       &lt;br /&gt;where ( T2376.STORENAME in       &lt;br /&gt;&lt;font color="#ff0000"&gt;('Store 1', 'Store 2', 'Store 3')&lt;/font&gt; )       &lt;br /&gt;order by c1&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;When D queries items, he sees just A and B:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/S4dBxJG3sUI/AAAAAAAAAvA/wcXZbSelSog/s1600-h/clip_image042%5B3%5D.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="clip_image042" border="0" alt="clip_image042" src="http://lh4.ggpht.com/_vx0ImXMybTU/S4dBxk6uvjI/AAAAAAAAAvE/JU33N6rXXXg/clip_image042_thumb.jpg?imgmax=800" width="68" height="60" /&gt;&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;The physical SQL includes the Product session variable values.&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;select distinct T2372.ITEMNAME as c1     &lt;br /&gt;from       &lt;br /&gt;RETAILITEMS T2372       &lt;br /&gt;where ( T2372.ITEMNAME in &lt;font color="#ff0000"&gt;('A', 'B')&lt;/font&gt; )       &lt;br /&gt;order by c1&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;When D includes the fact QS in the query, the results are &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/S4dBxxjlIlI/AAAAAAAAAvI/vKbRMMstxIg/s1600-h/clip_image044%5B3%5D.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="clip_image044" border="0" alt="clip_image044" src="http://lh5.ggpht.com/_vx0ImXMybTU/S4dBybq0qKI/AAAAAAAAAvM/MIqRsMCsqV0/clip_image044_thumb.jpg?imgmax=800" width="159" height="78" /&gt;&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;You can see that these results are correct by examining the relevant rows in the fact table for D.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/S4dByqDlpwI/AAAAAAAAAvQ/y9io7oAy5_w/s1600-h/clip_image046%5B3%5D.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="clip_image046" border="0" alt="clip_image046" src="http://lh3.ggpht.com/_vx0ImXMybTU/S4dBzOmrZkI/AAAAAAAAAvU/9i_iX0lUAgs/clip_image046_thumb.jpg?imgmax=800" width="131" height="244" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Note how the ComplexSecurityTable is included in the physical SQL.&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;select&amp;#160;&amp;#160; T2372.ITEMNAME&amp;#160;&amp;#160; as C1     &lt;br /&gt;,T2376.STORENAME as C2      &lt;br /&gt;,Sum(T2364.QS)&amp;#160;&amp;#160; as C3       &lt;br /&gt;from&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;br /&gt;&lt;font color="#ff0000"&gt;COMPLEXSECURITY T2567        &lt;br /&gt;&lt;/font&gt;,RETAILSTORES T2376       &lt;br /&gt;,RETAILITEMS T2372       &lt;br /&gt;,RETAILFACTS T2364       &lt;br /&gt;where       &lt;br /&gt;&lt;font color="#ff0000"&gt;T2364.ITEM = T2567.PRODUCT&lt;/font&gt;       &lt;br /&gt;and T2364.ITEM = T2372.ITEM       &lt;br /&gt;and T2364.store = T2376.store       &lt;br /&gt;&lt;font color="#ff0000"&gt;and T2364.store = T2567.store&lt;/font&gt;       &lt;br /&gt;and T2567.USERID = 'D'       &lt;br /&gt;and T2372.ITEMNAME in ('Product A','Product B')       &lt;br /&gt;and T2376.STORENAME in       &lt;br /&gt;('Store 1','Store 2','Store 3')       &lt;br /&gt;group by T2372.ITEMNAME       &lt;br /&gt;,T2376.STORENAME       &lt;br /&gt;order by C1 ,C2&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;There are many possible variations on this theme. The important points are:&lt;/p&gt;  &lt;p&gt;1) Use row-wise variables to filter dimensional queries.&lt;/p&gt;  &lt;p&gt;2) Use row-wise variables to filter fact table queries when the intersection of dimensions defined by the row-wise variables meets the security requirements.&lt;/p&gt;  &lt;p&gt;3) Use a table with the appropriate legal dimensional tuples to limit fact table queries. Join this table to the fact table physically and include it in the business model. Include this table in queries involving the logical fact table by configuring the security filter appropriately for the group with complex security requirements.&lt;/p&gt;  &lt;p&gt;4) When the complex security involves attributes at a higher level than the grain of the fact table, then include those legal tuples in the complex security table and join the complex security table to the dimension tables.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1481199104475905802-4169470990415251361?l=kpipartners.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/4169470990415251361'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/4169470990415251361'/><link rel='alternate' type='text/html' href='http://kpipartners.blogspot.com/2010/02/complex-row-level-security.html' title='Complex Row Level Security'/><author><name>Kurt Wolff</name><uri>http://www.blogger.com/profile/07922051559586431987</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><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/_vx0ImXMybTU/S4dBiC-tVrI/AAAAAAAAAsk/8PX5W7QLmYo/s72-c/clip_image002_thumb.jpg?imgmax=800' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-1481199104475905802.post-4641532691077393454</id><published>2010-01-12T16:02:00.001-08:00</published><updated>2010-01-12T16:05:04.153-08:00</updated><title type='text'>Days, Hours, Minutes, Seconds</title><content type='html'>&lt;p&gt;Suppose you have a series of datetime pairs. &lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/S00Nn61_JWI/AAAAAAAAAr8/ILHrPd8ogZM/s1600-h/image%5B9%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_vx0ImXMybTU/S00NoZBYuHI/AAAAAAAAAsA/mrPCuiXxOAA/image_thumb%5B3%5D.png?imgmax=800" width="221" height="76" /&gt;&lt;/a&gt;     &lt;br /&gt;The problem is to display the differences between DATE1 and DATE2 in days, hours, minutes, and seconds.&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/S00NoruxtCI/AAAAAAAAAsE/U1ivcPFRB1o/s1600-h/image%5B11%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_vx0ImXMybTU/S00NpNdHP6I/AAAAAAAAAsI/q018gLE7lxg/image_thumb%5B5%5D.png?imgmax=800" width="337" height="76" /&gt;&lt;/a&gt;     &lt;br /&gt;There are undoubtedly multiple solutions to this problem, and you might want to try solve it yourself before reading the approach in this post.&amp;#160; If you do, here's some Oracle SQL to create and populate a table with the base data.     &lt;br /&gt;CREATE TABLE TIMECALCTABLE     &lt;br /&gt;&amp;#160;&amp;#160; (&amp;#160;&amp;#160;&amp;#160; RNUM NUMBER,     &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; DATE1 DATE,     &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; DATE2 DATE    &lt;br /&gt;&amp;#160;&amp;#160; )    &lt;br /&gt;insert into TimeCalcTable (RNum, Date1, Date2) values (1, to_date('03-Jan-10 13:12:44','dd-Mon-yy hh24:mi:ss'), to_date('04-Jan-10 07:42:50', 'dd-Mon-yy hh24:mi:ss')); insert into TimeCalcTable(RNum, Date1, Date2) values (2, to_date('11-Jan-10 20:28:13','dd-Mon-yy hh24:mi:ss'),to_date('27-Jan-10 01:30:45', 'dd-Mon-yy hh24:mi:ss')); insert into TimeCalcTable(RNum, Date1, Date2) values (3, to_date('02-Mar-10 16:16:41','dd-Mon-yy hh24:mi:ss'),to_date('14-Mar-10 07:56:39', 'dd-Mon-yy hh24:mi:ss'));    &lt;br /&gt;&lt;/p&gt;  &lt;p&gt;If you tried this, you might have been tempted to start by first calculating the number of days between the two dates. If you used the timestampdiff function with the sql_tsi_day parameter, you would have seen results like this:&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/S00NpmtH7GI/AAAAAAAAAsM/epUIgCPGYdc/s1600-h/image%5B16%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_vx0ImXMybTU/S00Nqdq0YnI/AAAAAAAAAsQ/MYbAndK6GgI/image_thumb%5B8%5D.png?imgmax=800" width="327" height="91" /&gt;&lt;/a&gt;     &lt;br /&gt;The physical SQL generated for the calculation using the Oracle database is     &lt;br /&gt;Trunc(DATE2) - Trunc(DATE1)     &lt;br /&gt;– i.e. midnight of DATE2 minus midnight of DATE1. &lt;/p&gt;  &lt;p&gt; So, in the first row, it calculates a 1 day difference between DATE1 and DATE2, even though there is less than a 24 hour period between them. &lt;/p&gt;  &lt;p&gt;To use the Timestampdiff function to get accurate results, you need to calculate the difference in days using a smaller time parameter, e.g. sql_tsi_minute. The formula below uses the Timestampdiff function to return the number of minutes between the two columns, then divides that number by the number of minutes in a day (24 hours times 60 minutes) discarding any remainder with the floor function.   &lt;br /&gt;FLOOR( TIMESTAMPDIFF( SQL_TSI_MINUTE , &amp;quot;DATE1&amp;quot;, &amp;quot;DATE2&amp;quot;) / (24 * 60)) &lt;/p&gt;  &lt;p&gt;To calculate how many hours are left that are not included in days, take the difference in minutes between the two columns,&amp;#160; divide that by 60 to get hours, discard the remainder with the floor function, then subtract the number of hours already represented in days. &lt;/p&gt;  &lt;p&gt;FLOOR( TIMESTAMPDIFF( SQL_TSI_MINUTE , &amp;quot;DATE1&amp;quot;,&amp;quot;DATE2&amp;quot;) / 60) - 24 *&amp;#160; &lt;br /&gt;FLOOR( TIMESTAMPDIFF( SQL_TSI_MINUTE , &amp;quot;DATE1&amp;quot;, &amp;quot;DATE2&amp;quot;) / (24 * 60))&lt;/p&gt;  &lt;p&gt;&amp;#160; &lt;br /&gt; To calculate the number of minutes, find the difference in seconds, divide by 60, use the floor function to discard any remainder,&amp;#160; and subtract the minutes that have already been represented in hours and days.    &lt;br /&gt; FLOOR( TIMESTAMPDIFF( SQL_TSI_SECOND ,&amp;quot;DATE1&amp;quot;,&amp;quot;DATE2&amp;quot;) / 60)     &lt;br /&gt;- 60 *&amp;#160; FLOOR( TIMESTAMPDIFF( SQL_TSI_MINUTE , &amp;quot;DATE1&amp;quot;,&amp;quot;DATE2&amp;quot;) / 60)    &lt;br /&gt;&lt;/p&gt;  &lt;p&gt;Finally, to calculate seconds, calculate the difference in seconds and discard the seconds accounted for in minutes, hours, and days (i.e. the seconds in the whole minutes between the two columns).   &lt;br /&gt; TIMESTAMPDIFF( SQL_TSI_SECOND , &amp;quot;DATE1&amp;quot;,&amp;quot;DATE2&amp;quot;) - 60 *&amp;#160; FLOOR( TIMESTAMPDIFF( SQL_TSI_SECOND , &amp;quot;DATE1&amp;quot;, &amp;quot;DATE2&amp;quot;) / 60)    &lt;br /&gt;&lt;/p&gt;  &lt;p&gt;Now you can combine these formulas, casting results as varchars, and add text to show the time durations of days, hours, minutes, and seconds.&lt;/p&gt;  &lt;p&gt;   &lt;br /&gt;cast(floor(TimestampDiff(SQL_TSI_MINUTE, DATE1, DATE2) / (24 * 60)) as&amp;#160; VARCHAR ( 8 ))&amp;#160; || ' Days' || ' ' || cast(floor(TimestampDiff(SQL_TSI_MINUTE, DATE1, DATE2) /&amp;#160; 60) - floor(TimestampDiff(SQL_TSI_MINUTE, DATE1, DATE2) / (24 * 60) * 24 as&amp;#160; VARCHAR ( 8 ))&amp;#160; || ' Hours ' || cast(floor(TimestampDiff(SQL_TSI_SECOND, DATE1, DATE2) / 60) - floor(TimestampDiff(SQL_TSI_MINUTE, DATE1, DATE2) /&amp;#160; 60) * 60 as&amp;#160; VARCHAR ( 8 ))&amp;#160; || ' Minutes ' || cast(TimestampDiff(SQL_TSI_SECOND, DATE1, DATE2) - floor(TimestampDiff(SQL_TSI_SECOND, DATE1, DATE2) / 60) * 60 as&amp;#160; VARCHAR ( 8 ))&amp;#160; || ' Seconds' &lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/S00Nqod-TPI/AAAAAAAAAsU/dSE8qRi50kM/s1600-h/image%5B21%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_vx0ImXMybTU/S00NrfPrZAI/AAAAAAAAAsY/5mmbaRhJBzA/image_thumb%5B11%5D.png?imgmax=800" width="386" height="84" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1481199104475905802-4641532691077393454?l=kpipartners.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/4641532691077393454'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/4641532691077393454'/><link rel='alternate' type='text/html' href='http://kpipartners.blogspot.com/2010/01/days-hours-minutes-seconds.html' title='Days, Hours, Minutes, Seconds'/><author><name>Kurt Wolff</name><uri>http://www.blogger.com/profile/07922051559586431987</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><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/_vx0ImXMybTU/S00NoZBYuHI/AAAAAAAAAsA/mrPCuiXxOAA/s72-c/image_thumb%5B3%5D.png?imgmax=800' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-1481199104475905802.post-8155468232776682900</id><published>2009-12-28T22:42:00.001-08:00</published><updated>2009-12-28T22:42:15.953-08:00</updated><title type='text'>Defining and Assigning Web Group Memberships</title><content type='html'>&lt;p&gt;Suppose you have a dashboard with three pages (tabs). Suppose further that not every user should see all three tabs. Most users will see a limited set of the tabs – some will see two, some will see only one. You want to define web groups to cover all the possible combinations. How many web groups would you have to create?&lt;/p&gt;  &lt;p&gt;The combinations are:&lt;/p&gt;  &lt;p&gt;321   &lt;br /&gt;32    &lt;br /&gt;31    &lt;br /&gt;21    &lt;br /&gt;3    &lt;br /&gt;2    &lt;br /&gt;1&lt;/p&gt;  &lt;p&gt;So you would have to define 7 web groups.&lt;/p&gt;  &lt;p&gt;Suppose there are five tabs. With five tabs there are 31 combinations.&lt;/p&gt;  &lt;p&gt;Suppose there are ten tabs. Now there are 1023 combinations.&lt;/p&gt;  &lt;p&gt;In general, for a collection of n things, there are 2^n-1 combinations.&lt;/p&gt;  &lt;p&gt;Clearly, even with only 10 tabs, you cannot define web groups to cover all combinations.&lt;/p&gt;  &lt;p&gt;The problem is made more difficult by the fact that OBIEE only tells you which web groups have permission to access a web object. It does not tell you which objects a particular web group has permission to access. This means that, for any given user, you cannot predict what his/her experience will be after logging into OBIEE. &lt;/p&gt;  &lt;p&gt;Web security gets messy very fast.&lt;/p&gt;  &lt;p&gt;One way to deal with this is to create a single web group for every object. That means that there will be at most only n web groups, not 2^n-1. (Both n and 2^n-1 are theoretical combinations for read permission. There could be another set of web groups for full control or other permissions). Then assign membership to the web groups using row-wise initialization at log on.&lt;/p&gt;  &lt;p&gt;A sample table of users and web groups might contain these entries for users K and G. K is a member of Tab11, Tab13, and Tab15 web groups.&amp;#160; G is a member of Tab11, Tab12, Tab14, and Tab16.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/SzmkvxuP_tI/AAAAAAAAAqM/88PVsqus9bs/s1600-h/image%5B2%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_vx0ImXMybTU/SzmkwYq1x2I/AAAAAAAAAqQ/xqR-SYEwlRE/image_thumb.png?imgmax=800" width="244" height="143" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;When K logs on she sees these tabs:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/SzmkwnGAtcI/AAAAAAAAAqU/rQdUr78CTfs/s1600-h/image%5B7%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_vx0ImXMybTU/SzmkxBnBY1I/AAAAAAAAAqY/ETCvPkCNf7I/image_thumb%5B3%5D.png?imgmax=800" width="325" height="56" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;When G logs on, he sees these:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/SzmkxWMTqtI/AAAAAAAAAqc/leogsjaw3P4/s1600-h/image%5B12%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_vx0ImXMybTU/Szmkxwpk_3I/AAAAAAAAAqg/GWsP1s41iys/image_thumb%5B6%5D.png?imgmax=800" width="330" height="49" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;With this approach, it is also easy to predict (or audit) the experience of each user simply by querying the table. It would also be easy to create an application (outside of OBIEE) to assign permissions to users.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1481199104475905802-8155468232776682900?l=kpipartners.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/8155468232776682900'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/8155468232776682900'/><link rel='alternate' type='text/html' href='http://kpipartners.blogspot.com/2009/12/defining-and-assigning-web-group.html' title='Defining and Assigning Web Group Memberships'/><author><name>Kurt Wolff</name><uri>http://www.blogger.com/profile/07922051559586431987</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><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_vx0ImXMybTU/SzmkwYq1x2I/AAAAAAAAAqQ/xqR-SYEwlRE/s72-c/image_thumb.png?imgmax=800' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-1481199104475905802.post-4603779766708490604</id><published>2009-12-03T18:05:00.001-08:00</published><updated>2009-12-08T19:21:29.974-08:00</updated><title type='text'>The Aggregate Function</title><content type='html'>&lt;p&gt;In addition to familiar SQL aggregation functions such as Sum, Max, Min, Average, and Count, OBIEE also supports an aggregation function called “Aggregate”. This function is available for use in formulas written in Answers. &lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Business Model&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;To illustrate the use of the Aggregate function, consider the following simple business model that has one dimension and three facts. Two facts (“Qty Sold” and “Amt Sold”) have an aggregation rule of Sum in the metadata. “Avg Price” is defined as “Amt Sold”/“Qty Sold”.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/SxhuLGArLfI/AAAAAAAAAno/4tUWaamBhZQ/s1600-h/image5.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_vx0ImXMybTU/SxhuLpRCU6I/AAAAAAAAAns/K1v9j2at9C4/image_thumb3.png?imgmax=800" width="265" height="442" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;The business model has two tables as it sources, “Time” and “POSTest”.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/SxhuL8T4LuI/AAAAAAAAAnw/Kxxbgk5Jtsg/s1600-h/image111.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_vx0ImXMybTU/SxhuMJ7oJYI/AAAAAAAAAn0/DJPl5OgTaRc/image_thumb69.png?imgmax=800" width="240" height="45" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Query Using Sum&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Consider a query that calculates the Qty Sold for each month and also calculates the total Qty Sold for all month, using the SUM function in a formula.&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/SxhuMYZZmNI/AAAAAAAAAn4/9ybfmWaX5PQ/s1600-h/image10.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_vx0ImXMybTU/SxhuM3KXgEI/AAAAAAAAAn8/8qXmTnX683s/image_thumb6.png?imgmax=800" width="360" height="100" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;pre&gt;&lt;font face="Georgia"&gt;This query generated the following SQL. Interestingly, &lt;br /&gt;the value in the SUM QS column was calculated &lt;br /&gt;on the result set by OBIEE.&lt;/font&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;select T463.MONNAME as c1,&lt;br /&gt;     sum(T2144.QS) as c2,&lt;br /&gt;     T463.YEARMONTH as c4&lt;br /&gt;from &lt;br /&gt;     TIME T463,&lt;br /&gt;     POSTEST T2144&lt;br /&gt;where  ( T463.DATE_ = T2144.DATE1 ) &lt;br /&gt;group by T463.MONNAME, T463.YEARMONTH&lt;br /&gt;order by c1, c4&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;strong&gt;Query With Grand Total Using Default Rule&lt;/strong&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;If instead of using the SUM function in a formula we had simply requested a grand total, we would have seen this.&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/SxhuNBl_XZI/AAAAAAAAAoA/Dmkq0SnIfCA/s1600-h/image14.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_vx0ImXMybTU/SxhuNXcv5wI/AAAAAAAAAoE/okUzE1krheA/image_thumb8.png?imgmax=800" width="181" height="143" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;The logical query used the REPORT_SUM function:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;SELECT &amp;quot;Time Dim&amp;quot;.&amp;quot;Month Name&amp;quot; saw_0, Facts.&amp;quot;Qty Sold&amp;quot; saw_1, &lt;font color="#ff0000"&gt;&lt;strong&gt;REPORT_SUM(saw_1 BY )&lt;/strong&gt;&lt;/font&gt; FROM AggregateTest ORDER BY saw_0&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Again, if you look at the physical query, you can see that the grand total is being calculated by OBIEE from the result set.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;WITH &lt;br /&gt;SAWITH0 AS (&lt;font color="#ff0000"&gt;&lt;strong&gt;select sum(T2144.QS) as c1&lt;/strong&gt;&lt;/font&gt;,&lt;br /&gt;     T463.MONNAME as c2,&lt;br /&gt;     T463.YEARMONTH as c3&lt;br /&gt;from &lt;br /&gt;     TIME T463,&lt;br /&gt;     POSTEST T2144&lt;br /&gt;where  ( T463.DATE_ = T2144.DATE1 ) &lt;br /&gt;group by T463.MONNAME, T463.YEARMONTH)&lt;br /&gt;select distinct SAWITH0.c2 as c1,&lt;br /&gt;     &lt;strong&gt;&lt;font color="#ff0000"&gt;SAWITH0.c1 as c2&lt;/font&gt;&lt;/strong&gt;,&lt;br /&gt;     SAWITH0.c3 as c4,&lt;br /&gt;     &lt;font color="#ff0000"&gt;&lt;strong&gt;SAWITH0.c1 as c6&lt;/strong&gt;&lt;/font&gt;&lt;br /&gt;from &lt;br /&gt;     SAWITH0&lt;br /&gt;order by c1, c4&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;strong&gt;Query with Grand Total Using Server Complex Aggregate&lt;/strong&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;The same query, with the aggregation rule on Qty Sold changed to “Server Complex Aggregate”, produces a logical query using the AGGREGATE function. AGGREGATE is used with BY followed by a null, which means aggregate over all rows.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;SELECT &amp;quot;Time Dim&amp;quot;.&amp;quot;Month Name&amp;quot; saw_0, Facts.&amp;quot;Qty Sold&amp;quot; saw_1, &lt;font color="#ff0000"&gt;&lt;strong&gt;AGGREGATE(saw_1 BY )&lt;/strong&gt;&lt;/font&gt; FROM AggregateTest ORDER BY saw_0&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;The physical query generated by OBIEE was: &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;WITH &lt;br /&gt;SAWITH0 AS (select D1.c1 as c1,&lt;br /&gt;     D1.c2 as c2,&lt;br /&gt;     D1.c3 as c3&lt;br /&gt;from &lt;br /&gt;     (&lt;font color="#0000ff"&gt;&lt;strong&gt;&lt;em&gt;select sum(T2144.QS) as c1,&lt;br /&gt;               T463.MONNAME as c2,&lt;br /&gt;               T463.YEARMONTH as c3,&lt;br /&gt;               ROW_NUMBER() OVER (PARTITION BY T463.YEARMONTH ORDER BY T463.YEARMONTH ASC) as c4&lt;br /&gt;          from &lt;br /&gt;               TIME T463,&lt;br /&gt;               POSTEST T2144&lt;br /&gt;          where  ( T463.DATE_ = T2144.DATE1 ) &lt;br /&gt;          group by T463.MONNAME, T463.YEARMONTH&lt;/em&gt;&lt;/strong&gt;&lt;br /&gt;&lt;/font&gt;     ) D1&lt;br /&gt;where  ( D1.c4 = 1 ) ),&lt;br /&gt;SAWITH1 AS (select sum(T2144.QS) as c1&lt;br /&gt;from &lt;br /&gt;     POSTEST T2144)&lt;br /&gt;select SAWITH0.c2 as c1,&lt;br /&gt;     SAWITH0.c1 as c2,&lt;br /&gt;     SAWITH0.c3 as c4,&lt;br /&gt;     SAWITH1.c1 as c5&lt;br /&gt;from &lt;br /&gt;     SAWITH0,&lt;br /&gt;     SAWITH1&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;In this query, the derived table D1 evaluates to this:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/SxhuNeMO2mI/AAAAAAAAAoI/NPZE-KOToZ4/s1600-h/image18.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_vx0ImXMybTU/SxhuN6jU59I/AAAAAAAAAoM/lPtKtGrD-AE/image_thumb10.png?imgmax=800" width="173" height="140" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;SAWITH0 evaluates to:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/SxhuNxa23FI/AAAAAAAAAoQ/xyPVKEhgsWo/s1600-h/image24.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_vx0ImXMybTU/SxhuOCJTLKI/AAAAAAAAAoU/RSmwDOZUk3c/image_thumb14.png?imgmax=800" width="140" height="141" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;SAWITH1 evaluates to &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/SxhuOeIKaSI/AAAAAAAAAoY/yYA-K3A8RDg/s1600-h/image28.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_vx0ImXMybTU/SxhuOnAi1LI/AAAAAAAAAoc/EQxm-YlIGL0/image_thumb16.png?imgmax=800" width="41" height="49" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;The physical query taken as a whole evaluates to &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/SxhuO4DKg2I/AAAAAAAAAog/lpctmc3hTP0/s1600-h/image32.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_vx0ImXMybTU/SxhuPdWC1nI/AAAAAAAAAok/Rw9K4xsRfBo/image_thumb18.png?imgmax=800" width="176" height="138" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;There are a couple of things to notice here. One is that by using the Server Complex Aggregate rule, the aggregation formula in the logical query for the grand total changes from &lt;font color="#ff0000"&gt;&lt;strong&gt;REPORT_SUM(saw_1 BY )&amp;#160; &lt;/strong&gt;&lt;font color="#400040"&gt;to&lt;/font&gt;&lt;strong&gt; &lt;font color="#ff0000"&gt;AGGREGATE(saw_1 BY )&lt;/font&gt;.&lt;/strong&gt; &lt;/font&gt;&lt;font color="#000000"&gt;The physical query also changes in form. The grand total is not directly calculated in the physical SQL generated for Report_Sum, whereas it is calculated in a separate SQL block when the logical SQL uses Aggregate.&amp;#160; The Aggregate function is a message to the BI Server saying, essentially, “determine how to aggregate by using the information in the metadata”.&lt;/font&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;font color="#000000"&gt;&lt;strong&gt;Using the Aggregate Function in Answers&lt;/strong&gt;&lt;/font&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;font color="#000000"&gt;&lt;/font&gt;The fact that the presentation server generates logical SQL using the Aggregate function is a clue that the Aggregate function can be used in a formula in Answers (even though Aggregate is not listed as an aggregate function in the expression builder!).&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/SxhuPiOWFXI/AAAAAAAAAoo/ETDTDiBEkqM/s1600-h/image47.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_vx0ImXMybTU/SxhuQFHqjDI/AAAAAAAAAos/nAhW5AINMvM/image_thumb27.png?imgmax=800" width="315" height="270" /&gt;&lt;/a&gt;&amp;#160;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;When writing a formula with Aggregate, you can use “(..by)”.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/SxhuQcGD-2I/AAAAAAAAAow/QxbhrMQfzUU/s1600-h/image48.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_vx0ImXMybTU/SxhuQlMoh3I/AAAAAAAAAo0/oqXygVYw2L8/image_thumb28.png?imgmax=800" width="312" height="182" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Using this formula, here are the results:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/SxhuQysNbMI/AAAAAAAAAo4/3_cruVqxlIU/s1600-h/image45.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_vx0ImXMybTU/SxhuRVJ4I-I/AAAAAAAAAo8/vuoMbnFAIac/image_thumb25.png?imgmax=800" width="320" height="139" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;The logical SQL generated is almost the same as the logical SQL generated in the previous example, with the column label “Qty Sold” used instead of “saw_1”.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;SELECT &amp;quot;Time Dim&amp;quot;.&amp;quot;Month Name&amp;quot; saw_0, Facts.&amp;quot;Qty Sold&amp;quot; saw_1, Aggregate(Facts.&lt;font color="#ff0000"&gt;&lt;strong&gt;&amp;quot;Qty Sold&amp;quot;&lt;/strong&gt;&lt;/font&gt; by) saw_2 FROM AggregateTest ORDER BY saw_0&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;The physical SQL generated is exactly the same.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;strong&gt;Complex Aggregation Rule&lt;/strong&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Consider what happens when Avg Price (defined in the metadata as Amt Sold/Qty Sold)&amp;#160; is included in the query.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/SxhuRolDrFI/AAAAAAAAApA/pojiggXv2Co/s1600-h/image54.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_vx0ImXMybTU/SxhuR8TY2rI/AAAAAAAAApE/zuwAd9FOcDo/image_thumb32.png?imgmax=800" width="297" height="150" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;The BI Server generates the following physical SQL, with Avg Price calculated in the last Select block.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;WITH &lt;br /&gt;SAWITH0 AS (select sum(T2144.QS) as c1,&lt;br /&gt;     sum(T2144.AMT) as c2,&lt;br /&gt;     T463.MONNAME as c3,&lt;br /&gt;     T463.YEARMONTH as c4&lt;br /&gt;from &lt;br /&gt;     TIME T463,&lt;br /&gt;     POSTEST T2144&lt;br /&gt;where  ( T463.DATE_ = T2144.DATE1 ) &lt;br /&gt;group by T463.MONNAME, T463.YEARMONTH)&lt;br /&gt;select distinct SAWITH0.c3 as c1,&lt;br /&gt;     SAWITH0.c1 as c2,&lt;br /&gt;     &lt;strong&gt;&lt;font color="#ff0000"&gt;SAWITH0.c2 / nullif( SAWITH0.c1, 0) as c3&lt;/font&gt;&lt;/strong&gt;,&lt;br /&gt;     SAWITH0.c4 as c4&lt;br /&gt;from &lt;br /&gt;     SAWITH0&lt;br /&gt;order by c4&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;font face="Georgia"&gt;Now we’ll include a Grand Total in the query.&lt;/font&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/SxhuSIeCboI/AAAAAAAAApI/QCXckAIo0aI/s1600-h/image61.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_vx0ImXMybTU/SxhuSfWqLvI/AAAAAAAAApM/RQKjsRZgeu4/image_thumb37.png?imgmax=800" width="277" height="162" /&gt;&lt;/a&gt; &lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;The logical SQL generated uses the Aggregate function for Avg Price.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;SELECT &amp;quot;Time Dim&amp;quot;.&amp;quot;Month Name&amp;quot; saw_0, Facts.&amp;quot;Qty Sold&amp;quot; saw_1, Facts.&amp;quot;Avg Price&amp;quot; saw_2, REPORT_SUM(saw_1 BY ), &lt;font color="#ff0000"&gt;AGGREGATE(saw_2 BY )&lt;/font&gt; FROM AggregateTest ORDER BY saw_0&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;This results in the following physical SQL being generated.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;font face="Courier New"&gt;WITH &lt;br /&gt;    &lt;br /&gt;SAWITH0 AS (select D1.c1 as c1,&amp;#160; &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160; D1.c2 as c2,&amp;#160; &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160; D1.c3 as c3, &lt;br /&gt;&lt;br /&gt;    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160; D1.c4 as c4 &lt;br /&gt;&lt;br /&gt;    &lt;br /&gt;from&amp;#160; &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160; (select sum(T2144.QS) as c1,&amp;#160; &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; sum(T2144.AMT) as c2,&amp;#160; &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; T463.MONNAME as c3,&amp;#160; &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; T463.YEARMONTH as c4,&amp;#160; &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; ROW_NUMBER() OVER (PARTITION BY T463.YEARMONTH ORDER BY T463.YEARMONTH ASC) as c5&amp;#160; &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; from&amp;#160; &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; TIME T463,&amp;#160; &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; POSTEST T2144&amp;#160; &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; where&amp;#160; ( T463.DATE_ = T2144.DATE1 )&amp;#160; &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; group by T463.MONNAME, T463.YEARMONTH&amp;#160; &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160; ) D1 &lt;br /&gt;&lt;br /&gt;    &lt;br /&gt;where&amp;#160; ( D1.c5 = 1 ) ), &lt;br /&gt;&lt;br /&gt;    &lt;br /&gt;SAWITH1 AS (select sum(T2144.QS) as c1,&amp;#160; &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160; sum(T2144.AMT) as c2 &lt;br /&gt;&lt;br /&gt;    &lt;br /&gt;from &lt;br /&gt;&lt;br /&gt;    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160; POSTEST T2144) &lt;br /&gt;&lt;br /&gt;    &lt;br /&gt;select SAWITH0.c3 as c1,&amp;#160; &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160; SAWITH0.c1 as c2,&amp;#160; &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160; SAWITH0.c2 / nullif( SAWITH0.c1, 0) as c3,&amp;#160; &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160; SAWITH0.c1 as c4,&amp;#160; &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160; SAWITH0.c4 as c6,&amp;#160; &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160; SAWITH1.c2 as c7,&amp;#160; &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160; SAWITH1.c1 as c8 &lt;br /&gt;&lt;br /&gt;    &lt;br /&gt;from&amp;#160; &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160; SAWITH0,&amp;#160; &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160; SAWITH1&lt;/font&gt; &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;SAWITH0 evaluates to&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/SxhuSlhSN9I/AAAAAAAAApQ/3wu3JWeckxk/s1600-h/image66.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_vx0ImXMybTU/SxhuTEdDw9I/AAAAAAAAApU/NDKOzkWz_8c/image_thumb40.png?imgmax=800" width="232" height="180" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;SAWITH1 evaluates to&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/SxhuTWkc25I/AAAAAAAAApY/CPPV1_iLghc/s1600-h/image72.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_vx0ImXMybTU/SxhuTusEl5I/AAAAAAAAApc/hBnNCm8xT0U/image_thumb44.png?imgmax=800" width="121" height="75" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;The result set for whole query evaluates to&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/SxhuTxMcgII/AAAAAAAAApg/1c14v5ph6b4/s1600-h/image78.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_vx0ImXMybTU/SxhuUFapf5I/AAAAAAAAApk/Hgtur80RJm4/image_thumb48.png?imgmax=800" width="404" height="125" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Notice that the physical query does not calculate the grand total for Avg Price directly. The BI Server calculates the result (20.26) from these results as 12842/634 (or c7/c8).&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;We can use the Aggregate function directly in Answers by writing a formula like this:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/SxhuUd1jWpI/AAAAAAAAApo/YHTIFZuqVao/s1600-h/image84.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_vx0ImXMybTU/SxhuUrxBl6I/AAAAAAAAAps/cz5o5kEyi8Q/image_thumb52.png?imgmax=800" width="341" height="193" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/SxhuVPqne6I/AAAAAAAAApw/VNUbr56NKHo/s1600-h/image96.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_vx0ImXMybTU/SxhuVdlsbqI/AAAAAAAAAp0/BNNq5bUhUp8/image_thumb60.png?imgmax=800" width="350" height="128" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Using Aggregate we could write a formula to compare the monthly Avg Price to the Avg Price for the whole time period.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/SxhuVlp8JiI/AAAAAAAAAp4/lME5n03TaYY/s1600-h/image102.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_vx0ImXMybTU/SxhuV9T1jiI/AAAAAAAAAp8/aRPYl4xcvUU/image_thumb64.png?imgmax=800" width="349" height="160" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/SxhuWA3zlnI/AAAAAAAAAqA/wmdLmomGw6U/s1600-h/image107.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_vx0ImXMybTU/SxhuWRzvpfI/AAAAAAAAAqE/ZzYt1koaiJQ/image_thumb67.png?imgmax=800" width="381" height="114" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Knowing how to use the AGGREGATE function can be a useful addition to your OBIEE tool set.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1481199104475905802-4603779766708490604?l=kpipartners.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/4603779766708490604'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/4603779766708490604'/><link rel='alternate' type='text/html' href='http://kpipartners.blogspot.com/2009/12/aggregate-function.html' title='The Aggregate Function'/><author><name>Kurt Wolff</name><uri>http://www.blogger.com/profile/07922051559586431987</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><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/_vx0ImXMybTU/SxhuLpRCU6I/AAAAAAAAAns/K1v9j2at9C4/s72-c/image_thumb3.png?imgmax=800' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-1481199104475905802.post-7211744603845945650</id><published>2009-11-05T23:52:00.001-08:00</published><updated>2009-11-05T23:52:01.440-08:00</updated><title type='text'>“Tool Tips” in Column Headers</title><content type='html'>&lt;p&gt;What is the formula behind a measure? What is the source of the column in the report? These kinds of questions could be handled in multiple ways. The data dictionary feature is one, although a common reaction to the data dictionary feature is that it’s more suited to IT than to the business users. Another way is to present a “tool tip” in Answers (the text of which comes from the Description property of the logical or presentation column in the metadata). However, many users do not have access to Answers or mainly encounter the columns while perusing reports in a dashboard. &lt;/p&gt;  &lt;p&gt;A pretty good solution is to create a “tool tip” that appears when a mouse hovers over a column heading in a table, as shown here.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/SvPVkenNicI/AAAAAAAAAnA/lQCViEflBJA/s1600-h/image%5B4%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_vx0ImXMybTU/SvPVk8J-k4I/AAAAAAAAAnE/4oG-WGbROow/image_thumb%5B2%5D.png?imgmax=800" width="388" height="113" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;To present a tool tip in a column header, create a custom heading for the column using the html span tag, like this:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/SvPVlGd7VCI/AAAAAAAAAnI/upZ_CtOSF9o/s1600-h/image%5B15%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_vx0ImXMybTU/SvPVljjf_JI/AAAAAAAAAnM/tJSeQrtlKPQ/image_thumb%5B11%5D.png?imgmax=800" width="379" height="250" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;The full text of the column heading in this case is:&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;&lt;strong&gt;&amp;lt;span title=&amp;quot;Three letter abbreviations for the calendar months&amp;quot;&amp;gt;Month Name&amp;lt;/span&amp;gt;&lt;/strong&gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;The title, enclosed in double quotes, will become the tool tip when a mouseover occurs. &lt;/p&gt;  &lt;p&gt;This technique will work with virtually every browser, including IE, Firefox, Opera, Chrome, and Safari. (Which is not to say that Opera, Chrome, and Safari will necessarily be fully compatible with OBIEE). Tool tips will appear in pivot tables as well as in tables:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/SvPVlyFZZrI/AAAAAAAAAnQ/rnktZxb4Aso/s1600-h/image%5B24%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_vx0ImXMybTU/SvPVmYTysOI/AAAAAAAAAnU/C4Hdf6HY7AA/image_thumb%5B16%5D.png?imgmax=800" width="367" height="192" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;However, there are a few limitations. One is that you cannot control how long the tool tip appears (as far as I know). It appears for about 5 seconds. So limit the amount of text in the span title.&lt;/p&gt;  &lt;p&gt;Tool tips will not appear in charts or column selectors. In charts, the entire tag will appear in an axis title unless you delete it&amp;#160; in the Axis Title control . &lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/SvPVmx8KbNI/AAAAAAAAAnY/_2t9ZMivLSg/s1600-h/image%5B47%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_vx0ImXMybTU/SvPVnFBXhCI/AAAAAAAAAnc/B_45XC_PsLo/image_thumb%5B29%5D.png?imgmax=800" width="326" height="224" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/SvPVnrfyn0I/AAAAAAAAAng/qYNINIc5vfA/s1600-h/image%5B42%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_vx0ImXMybTU/SvPVoMLjmJI/AAAAAAAAAnk/9dxd5jqRwMY/image_thumb%5B26%5D.png?imgmax=800" width="332" height="134" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Another limitation is that you have to allow “cross-site scripting”. The instanceconfig.xml file must include the following tag:&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&amp;lt;HardenXSS&amp;gt;false&amp;lt;/HardenXSS&amp;gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Place this somewhere between &amp;lt;ServerInstance&amp;gt; and &amp;lt;/ServerInstance&amp;gt; and restart the presentation server. OBIEE, by default, prevents cross-site scripting. &lt;/p&gt;  &lt;p&gt;Before deciding to set HardenXSS to false, you should be sure that you understand the potential security vulnerabilities that this opens up.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1481199104475905802-7211744603845945650?l=kpipartners.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/7211744603845945650'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/7211744603845945650'/><link rel='alternate' type='text/html' href='http://kpipartners.blogspot.com/2009/11/tool-tips-in-column-headers.html' title='“Tool Tips” in Column Headers'/><author><name>Kurt Wolff</name><uri>http://www.blogger.com/profile/07922051559586431987</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><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/_vx0ImXMybTU/SvPVk8J-k4I/AAAAAAAAAnE/4oG-WGbROow/s72-c/image_thumb%5B2%5D.png?imgmax=800' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-1481199104475905802.post-3906426721538087176</id><published>2009-10-14T18:29:00.001-07:00</published><updated>2009-11-05T22:42:49.141-08:00</updated><title type='text'>Structuring Web Catalog Folders for Analytic Applications</title><content type='html'>&lt;p&gt;If you have tried to develop dashboards or coherent sets of reports as an analytic application, you have had to think about how best to structure a web catalog.&lt;/p&gt;  &lt;p&gt;A way to classify queries used in an analytic application is to group them according to their function: analyses that appear on dashboards, analyses that are navigation targets, analyses that provide dimensional sets, analyses that serve as triggers, and dashboard prompts.&lt;/p&gt;  &lt;p&gt;It makes sense to structure the web catalog to reflect these five classifications. With a top level folder for each subject area, the web cat folder structure looks like this:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/StZ679MzjCI/AAAAAAAAAmY/yrZxamP3t3Y/s1600-h/image%5B2%5D.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_vx0ImXMybTU/StZ68C2GdYI/AAAAAAAAAmc/58tcOtDOfNY/image_thumb.png?imgmax=800" width="161" height="94" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;The advantages of this structure are that it makes it easy to separate what actually appears on the dashboards from everything else that you may need to create to make the dashboards work as you want them to.&lt;/p&gt;  &lt;p&gt;“Analyses” will contain the largest number of objects. You may be tempted to create another level of folders under this, but in general I would argue that another level of folders just complicates the task of&amp;#160; finding things.&lt;/p&gt;  &lt;p&gt;By putting navigation targets in their own folder, they are not only easier to find (and protect), but if you have to migrate objects from one catalog to another, this structure makes it easier to migrate these objects first. When you migrate the other analyses their navigation targets will already be in the new web catalog (thus avoiding error messages you will receive if the navigation targets do not exist). &lt;/p&gt;  &lt;p&gt;The “Prompts” folder contains dashboard prompts. I find that I constantly separate these out, mentally, when they are intermingled with analyses that provide results and data visualizations. Putting them in their own folder saves me from continually having to do that.&lt;/p&gt;  &lt;p&gt;Sets are simply queries that provide dimensional members when you create a filter using the &lt;strong&gt;Advanced|Filter based on results of another request&lt;/strong&gt; option. Users never see them directly, but their use can be critical, especially when you have analyses that contain filters on measures and that also contain grand totals.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/StZ68SIPS5I/AAAAAAAAAmg/lAa862vqryk/s1600-h/image%5B7%5D.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_vx0ImXMybTU/StZ69FV4VWI/AAAAAAAAAmk/1sTEzue92A0/image_thumb%5B3%5D.png?imgmax=800" width="354" height="74" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;The “Triggers” folder contains another group of queries that users never see. These are simple queries that take a sub-second to execute and whose results (or lack of results) cause dashboard sections to appear or disappear, so-called “Guided Navigation Sections”.&amp;#160; (&lt;strong&gt;&lt;em&gt;Note&lt;/em&gt;&lt;/strong&gt;: queries in a Guided Navigation sections still run when the user clicks on a dashboard page, even if the Guided Navigation section has not been triggered to appear. Hopefully, in the not too distant future, Oracle will correct this behavior.)&lt;/p&gt;  &lt;p&gt;Dashboards themselves can exist in their own folder or can be grouped together in a common Shared Dashboards folder. The basic objective is to create a structure that makes assigning correct permissions to dashboards as easy as possible without inadvertently assigning permissions you do not intend. Since dashboards can (and often do) contain analyses that refer to more than one subject area, separating dashboards from the folders built around subject area content seems logical. &lt;/p&gt;  &lt;p&gt;If you use a common Shared Dashboards as the outer folder for dashboards, give Everyone Read permission to it.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/StZ69bY3VlI/AAAAAAAAAmo/OkRkqWs2vys/s1600-h/image%5B15%5D.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_vx0ImXMybTU/StZ691mHGSI/AAAAAAAAAms/N6opoJGBzwE/image_thumb%5B9%5D.png?imgmax=800" width="373" height="178" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Then give Everyone Read permission to the _portal folder.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/StZ6-GYRexI/AAAAAAAAAmw/jksYd_QyRCU/s1600-h/image%5B20%5D.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_vx0ImXMybTU/StZ6-pN3TcI/AAAAAAAAAm0/QtoFmlpBTzc/image_thumb%5B12%5D.png?imgmax=800" width="396" height="156" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Finally, assign the appropriate permissions to each dashboard in the _portal folder.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/StZ6_KICY9I/AAAAAAAAAm4/oLOvuVt_xjw/s1600-h/image%5B24%5D.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_vx0ImXMybTU/StZ6_XPzbdI/AAAAAAAAAm8/D1h133k2soE/image_thumb%5B14%5D.png?imgmax=800" width="404" height="149" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1481199104475905802-3906426721538087176?l=kpipartners.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/3906426721538087176'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/3906426721538087176'/><link rel='alternate' type='text/html' href='http://kpipartners.blogspot.com/2009/10/structuring-web-catalog-folders-for.html' title='Structuring Web Catalog Folders for Analytic Applications'/><author><name>Kurt Wolff</name><uri>http://www.blogger.com/profile/07922051559586431987</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><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/_vx0ImXMybTU/StZ68C2GdYI/AAAAAAAAAmc/58tcOtDOfNY/s72-c/image_thumb.png?imgmax=800' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-1481199104475905802.post-6872701039822982148</id><published>2009-09-13T13:59:00.001-07:00</published><updated>2009-09-19T09:01:03.915-07:00</updated><title type='text'>WriteBack in OBIEE</title><content type='html'>&lt;h3&gt;&amp;#160;&lt;/h3&gt;  &lt;p&gt;“Writeback” – presenting an Answers interface so that users can update data in database tables or insert new rows – first appeared in version 7.8.4. To use OBIEE’s writeback capabilities, there are several set up steps. While each one is simple and fairly straightforward, it’s easy to forget all the steps and where each one is located. This document contains the instructions in case you find yourself forgetting them. Since many people like to demo using Excel as a data source, it focuses first on using Excel. Later, an example using an Oracle table is discussed. &lt;/p&gt;  &lt;h4&gt;Sample Data&lt;/h4&gt;  &lt;p&gt;Here’s some data from an Excel 2003 workbook. The data includes columns with three different data types: numbers, character, and dates. The data is from a named range called “Data” in the Excel workbook.(DSN)&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/Sq1cx0o3uAI/AAAAAAAAAiU/3jdglDuS1Vw/s1600-h/image%5B7%5D.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_vx0ImXMybTU/Sq1cyq3ayhI/AAAAAAAAAiY/WjuLX28jklw/image_thumb%5B5%5D.png?imgmax=800" width="227" height="359" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Be sure that the Excel ODBC Data Source Name is not configured as Read Only.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/Sq1czZZ7YAI/AAAAAAAAAic/AvyoBSZY3v4/s1600-h/clip_image002%5B4%5D.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="clip_image002" border="0" alt="clip_image002" src="http://lh3.ggpht.com/_vx0ImXMybTU/Sq1c0HtZ8sI/AAAAAAAAAig/DDlEhlxWsyc/clip_image002_thumb%5B1%5D.jpg?imgmax=800" width="358" height="267" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;h4&gt;Create a Business Model and Presentation Catalog&lt;/h4&gt;  &lt;p&gt;I created a simple business model to use the sample data that looked like this:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/Sq1c0kqZjoI/AAAAAAAAAik/7bYZilcCoAQ/s1600-h/clip_image004%5B8%5D.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="clip_image004" border="0" alt="clip_image004" src="http://lh5.ggpht.com/_vx0ImXMybTU/Sq1c1BmPugI/AAAAAAAAAio/s_g9FzLUYQE/clip_image004_thumb%5B5%5D.jpg?imgmax=800" width="207" height="244" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Note that logical column Col1 is defined as the key. This isn’t critical, but if a key exists that maps to a column that is a key in the physical source table, it makes the resulting update SQL easier to write.&lt;/p&gt;  &lt;p&gt;Both logical tables “Dim” and “Facts” use the same physical source, which is the Excel “table” (named range) called “Data” defined in the physical layer of the metadata. The physical column “Col1” is the functional key, and it could be shown as the key in the metadata, but it doesn’t really matter if it is or not.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/Sq1c1qsOvBI/AAAAAAAAAis/JFy01B6Pqkk/s1600-h/clip_image006%5B4%5D.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="clip_image006" border="0" alt="clip_image006" src="http://lh6.ggpht.com/_vx0ImXMybTU/Sq1c12U1YvI/AAAAAAAAAiw/AqXvmL41Xco/clip_image006_thumb%5B1%5D.jpg?imgmax=800" width="225" height="130" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;To make it easier to verify that the writeback process is working, disable connection pooling using the checkbox on the Connection Pool properties dialog General tab.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/Sq1c2RZBNLI/AAAAAAAAAi0/XSnwLcsE1nA/s1600-h/clip_image008%5B5%5D.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="clip_image008" border="0" alt="clip_image008" src="http://lh4.ggpht.com/_vx0ImXMybTU/Sq1c2zr7sgI/AAAAAAAAAi4/BILIFUBKIUk/clip_image008_thumb%5B2%5D.jpg?imgmax=800" width="323" height="214" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;You may have noticed here another tab called “Write Back”. Ignore it. That tab is for other use cases where the BI Server has to write back to the database.&lt;/p&gt;  &lt;h4&gt;Enable Write Back Privilege&lt;/h4&gt;  &lt;p&gt;With web administration, enable the privilege to “Write Back to Database”. &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/Sq1c3QbiV7I/AAAAAAAAAi8/mgwWcS8sMAs/s1600-h/clip_image010%5B5%5D.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="clip_image010" border="0" alt="clip_image010" src="http://lh6.ggpht.com/_vx0ImXMybTU/Sq1c3zrNzrI/AAAAAAAAAjA/RTgeq2i71ns/clip_image010_thumb%5B2%5D.jpg?imgmax=800" width="403" height="36" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;h4&gt;Create a Query with a Table View&lt;/h4&gt;  &lt;p&gt;Here is the query that will be used to update the Excel workbook. This query includes all the columns from the physical table, but for updates this would not be necessary.&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/Sq1c4Yr9FyI/AAAAAAAAAjE/P0oXykR7Xao/s1600-h/clip_image012%5B6%5D.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="clip_image012" border="0" alt="clip_image012" src="http://lh4.ggpht.com/_vx0ImXMybTU/Sq1c4yXHllI/AAAAAAAAAjI/lWuhC7BvMSc/clip_image012_thumb%5B3%5D.jpg?imgmax=800" width="387" height="163" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Settle on the query structure, and save it, before going on to the next step.&lt;/p&gt;  &lt;h4&gt;Create a “Write Back Template”&lt;/h4&gt;  &lt;p&gt;The SQL the BI Server will use to generate the updates to the physical table comes from a template you will write. This template, the “Write Back Template”, is an XML file. For OBI EE versions 10.x, this file should be in the OracleBIData\web\msgdb\customMessages folder. This is true whether IIS or OC4J is the web server being used. The full path and name of this file is OracleBIData\web\msgdb\customMessages\WriteBackTemplate.xml.&lt;/p&gt;  &lt;p&gt;Here is the text of the file, and it actually contains three separate templates. They are called “UpdateExcelData”, “UpdateExcelCharData”, and “UpdateExcelDateTime”. Each template is a separate WebMessage name. Each template will be used by a different query to illustrate how to update columns of different data types. I included comments to make it easier to understand the mapping of the logical columns to the physical columns, which makes the SQL update and insert templates easier to understand. &lt;/p&gt;  &lt;p&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;utf-8&amp;quot; ?&amp;gt;   &lt;br /&gt;&amp;lt;WebMessageTables xmlns:sawm=&amp;quot;com.siebel.analytics.web.messageSystem&amp;quot;&amp;gt;    &lt;br /&gt;&amp;lt;WebMessageTable lang=&amp;quot;en-us&amp;quot; system=&amp;quot;WriteBackTemplates&amp;quot; table=&amp;quot;Templates&amp;quot;&amp;gt;    &lt;br /&gt;&amp;lt;!-- Testing templates used for writing back into an Excel workbook used as a data source --&amp;gt;    &lt;br /&gt;&amp;lt;!-- Save this file in the OracleBIData\web\msgdb\customMessages folder as WriteBackTemplate.xml --&amp;gt;    &lt;br /&gt;&amp;lt;WebMessage name= &amp;quot;UpdateExcelData&amp;quot;&amp;gt;    &lt;br /&gt;&amp;lt;XML&amp;gt;    &lt;br /&gt;&amp;lt;writeBack connectionPool=&amp;quot;WriteBack&amp;quot;&amp;gt;    &lt;br /&gt;&amp;lt;!-- Table has columns Col1, Col2, Col3Char, Col4Date&amp;#160; and in query c0 maps to Col1, c1 maps to Col3Char, c2 maps to Col4Date, c3 maps to Col2--&amp;gt;    &lt;br /&gt;&amp;lt;insert&amp;gt;INSERT INTO Data VALUES(@{c0},'@{c1}',@{c2},@{c3})&amp;lt;/insert&amp;gt;&amp;#160;&amp;#160;&amp;#160; &lt;br /&gt;&amp;lt;update&amp;gt;UPDATE Data SET &amp;quot;Col2&amp;quot;=@{c3} WHERE &amp;quot;Col1&amp;quot;=@{c0}&amp;lt;/update&amp;gt;    &lt;br /&gt;&amp;lt;/writeBack&amp;gt;    &lt;br /&gt;&amp;lt;/XML&amp;gt;    &lt;br /&gt;&amp;lt;/WebMessage&amp;gt;    &lt;br /&gt;&amp;lt;WebMessage name= &amp;quot;UpdateExcelCharData&amp;quot;&amp;gt;    &lt;br /&gt;&amp;lt;XML&amp;gt;    &lt;br /&gt;&amp;lt;writeBack connectionPool=&amp;quot;WriteBack&amp;quot;&amp;gt;    &lt;br /&gt;&amp;lt;insert&amp;gt;INSERT INTO Data VALUES(@{c0},'@{c1}',@{c2},@{c3})&amp;lt;/insert&amp;gt;    &lt;br /&gt;&amp;lt;!-- Table has columns Col1, Col2, Col3Char, Col4Date&amp;#160; and in query c0 maps to Col1, c1 maps to Col3Char, c2 maps to Col4Date, c3 maps to Col2--&amp;gt;    &lt;br /&gt;&amp;lt;update&amp;gt;UPDATE Data SET &amp;quot;Col3Char&amp;quot;='@{c1}' WHERE &amp;quot;Col1&amp;quot;=@{c0}&amp;lt;/update&amp;gt;    &lt;br /&gt;&amp;lt;/writeBack&amp;gt;    &lt;br /&gt;&amp;lt;/XML&amp;gt;    &lt;br /&gt;&amp;lt;/WebMessage&amp;gt;    &lt;br /&gt;&amp;lt;WebMessage name= &amp;quot;UpdateExcelDateTime&amp;quot;&amp;gt;    &lt;br /&gt;&amp;lt;XML&amp;gt;    &lt;br /&gt;&amp;lt;writeBack connectionPool=&amp;quot;WriteBack&amp;quot;&amp;gt;    &lt;br /&gt;&amp;lt;insert&amp;gt;INSERT INTO Data VALUES(@{c0},'@{c1}',@{c2},@{c3})&amp;lt;/insert&amp;gt;    &lt;br /&gt;&amp;lt;!-- Table has columns Col1, Col2, Col3Char, Col4Date&amp;#160; and in query c0 maps to Col1, c1 maps to Col3Char, c2 maps to Col4Date, c3 maps to Col2--&amp;gt;    &lt;br /&gt;&amp;lt;update&amp;gt;UPDATE Data SET &amp;quot;Col4Date&amp;quot;= '@{c2}' WHERE &amp;quot;Col1&amp;quot;=@{c0}&amp;lt;/update&amp;gt;    &lt;br /&gt;&amp;lt;/writeBack&amp;gt;    &lt;br /&gt;&amp;lt;/XML&amp;gt;    &lt;br /&gt;&amp;lt;/WebMessage&amp;gt;    &lt;br /&gt;&amp;lt;/WebMessageTable&amp;gt;    &lt;br /&gt;&amp;lt;/WebMessageTables&amp;gt;&lt;/p&gt;  &lt;p&gt;Note that @{c0} means the column in the first position on the Criteria tab. The positional columns are defined at the time you first save the query. If you rearrange column order, (e.g. moving the first column to the second position), the value of n in @{cn} does &lt;b&gt;&lt;i&gt;not&lt;/i&gt;&lt;/b&gt; change. &lt;/p&gt;  &lt;p&gt;Columns that are a character (text) datatype have single quotes around them. Dates or datetime columns also require single quotes around them for Excel. The form of the SQL templates, particularly for datetime columns, will depend on the target database platform.&lt;/p&gt;  &lt;p&gt;What does table=&amp;quot;Templates&amp;quot; mean? Its an XML thing. It tells what style of table is being created. In this case a template.&lt;/p&gt;  &lt;h4&gt;Configure Table Write Back Properties&lt;/h4&gt;  &lt;p&gt;Using the query you created and saved, go to the table view and click on the Write Back Properties icon.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/Sq1c5OZdDtI/AAAAAAAAAjM/LnAWcmeOFO0/s1600-h/clip_image014%5B4%5D.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="clip_image014" border="0" alt="clip_image014" src="http://lh4.ggpht.com/_vx0ImXMybTU/Sq1c5u0YpRI/AAAAAAAAAjQ/aKEJ4DSRtgU/clip_image014_thumb%5B1%5D.jpg?imgmax=800" width="214" height="140" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;When the Write Back Dialog opens, check Enable Write Back and enter the “Template Name”. The &amp;quot;template name&amp;quot; is the name of the message in the template file, not the file name itself. Each WebMessage element has a name attribute. In the example above, there are three Webmessage names: “UpdateExcelData”, “UpdateExcelCharData”, and “UpdateExcelDateTime”. For this first query, enter UpdateExcelData. This contains a SQL template for updating a numeric column.&lt;/p&gt;  &lt;p&gt;Enter the button text and select the button position.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/Sq1c6LrrCMI/AAAAAAAAAjU/3b8dE8K6zvk/s1600-h/clip_image016%5B4%5D.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="clip_image016" border="0" alt="clip_image016" src="http://lh6.ggpht.com/_vx0ImXMybTU/Sq1c6gHegVI/AAAAAAAAAjY/_0UY1kjDLdY/clip_image016_thumb%5B1%5D.jpg?imgmax=800" width="317" height="237" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;h4&gt;Set Column Interaction as Write Back&lt;/h4&gt;  &lt;p&gt;In the Criteria tab, click the properties button on the column you want to update.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/Sq1c642DLKI/AAAAAAAAAjc/6etm9Bxj1mo/s1600-h/clip_image018%5B4%5D.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="clip_image018" border="0" alt="clip_image018" src="http://lh3.ggpht.com/_vx0ImXMybTU/Sq1c7cLwYPI/AAAAAAAAAjg/DfXZqLsw4qE/clip_image018_thumb%5B1%5D.jpg?imgmax=800" width="333" height="89" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;On the second tab of the column properties dialog, “Column Format”, select Write Back as the Value Interaction. Enter the field size. Here the field size will hold 6 characters.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/Sq1c7u0BmwI/AAAAAAAAAjk/zFPhwl7Y39Y/s1600-h/clip_image020%5B4%5D.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="clip_image020" border="0" alt="clip_image020" src="http://lh4.ggpht.com/_vx0ImXMybTU/Sq1c8L7eaZI/AAAAAAAAAjo/aBXFz6OZK0E/clip_image020_thumb%5B1%5D.jpg?imgmax=800" width="331" height="313" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Save the query. Log out of Answers.&lt;/p&gt;  &lt;h4&gt;Restart the Presentation Server&lt;/h4&gt;  &lt;p&gt;The writeback template will not be available to use until after you restart the presentation server. You have to restart each time you change the WriteBackTemplate.xml fle.&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/Sq1c8pf5NbI/AAAAAAAAAjs/Bnwhagkf5HU/s1600-h/clip_image022%5B6%5D.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="clip_image022" border="0" alt="clip_image022" src="http://lh6.ggpht.com/_vx0ImXMybTU/Sq1c9Xdvr-I/AAAAAAAAAjw/mHvvDq6SUoc/clip_image022_thumb%5B3%5D.jpg?imgmax=800" width="397" height="137" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Log back in and run the query. &lt;/p&gt;  &lt;h4&gt;Verify Write Back Is Working&lt;/h4&gt;  &lt;p&gt;Run the query. The existing data appears.&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/Sq1c9z9XFGI/AAAAAAAAAj0/IpRtwwVVnMw/s1600-h/clip_image023%5B4%5D.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="clip_image023" border="0" alt="clip_image023" src="http://lh6.ggpht.com/_vx0ImXMybTU/Sq1c-MVUvmI/AAAAAAAAAj4/y_hhK0MxZdY/clip_image023_thumb%5B1%5D.jpg?imgmax=800" width="318" height="164" /&gt;&lt;/a&gt;     &lt;br /&gt;    &lt;br /&gt;Edit the cell whose value you want to change.&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/Sq1c-rSnMoI/AAAAAAAAAj8/hWeM4sC--j0/s1600-h/clip_image024%5B4%5D.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="clip_image024" border="0" alt="clip_image024" src="http://lh5.ggpht.com/_vx0ImXMybTU/Sq1c_NhzLWI/AAAAAAAAAkA/f3p2Lwya_v4/clip_image024_thumb%5B1%5D.jpg?imgmax=800" width="326" height="156" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Click the update button. &lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/Sq1c_YgFLUI/AAAAAAAAAkE/JWTEN5rGe5E/s1600-h/clip_image026%5B4%5D.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="clip_image026" border="0" alt="clip_image026" src="http://lh5.ggpht.com/_vx0ImXMybTU/Sq1dAJJ3P-I/AAAAAAAAAkM/QM8Uivr3520/clip_image026_thumb%5B1%5D.jpg?imgmax=800" width="161" height="71" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Open the Excel workbook and verify the change.&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/Sq1dAbiABOI/AAAAAAAAAkQ/Jl7fKBYvVE8/s1600-h/clip_image028%5B4%5D.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="clip_image028" border="0" alt="clip_image028" src="http://lh6.ggpht.com/_vx0ImXMybTU/Sq1dA8TJD6I/AAAAAAAAAkU/DTtxNN2ii_I/clip_image028_thumb%5B1%5D.jpg?imgmax=800" width="332" height="127" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Close the Excel workbook. (Leaving the Excel workbook open will cause an error when you try to query it again). &lt;/p&gt;  &lt;h4&gt;Configure Other Queries to Update Other Columns&lt;/h4&gt;  &lt;p&gt;I modified the first query and saved two other queries in order to illustrate updating character and datetime columns. Each of these queries uses a separate template: “UpdateExcelCharData” and “UpdateExcelDateTime”.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/Sq1dBeV8t3I/AAAAAAAAAkY/2Piu2z51eQc/s1600-h/clip_image030%5B5%5D.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="clip_image030" border="0" alt="clip_image030" src="http://lh3.ggpht.com/_vx0ImXMybTU/Sq1dB-3a5zI/AAAAAAAAAkc/p9qkeUMpD4Y/clip_image030_thumb%5B2%5D.jpg?imgmax=800" width="349" height="350" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/Sq1dCbB-E7I/AAAAAAAAAkg/gmgbv7V_Ha0/s1600-h/clip_image032%5B9%5D.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="clip_image032" border="0" alt="clip_image032" src="http://lh5.ggpht.com/_vx0ImXMybTU/Sq1dDIFz7hI/AAAAAAAAAkk/C0eu8Vpmrok/clip_image032_thumb%5B6%5D.jpg?imgmax=800" width="352" height="364" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Open the Excel workbook to verify the update occurred.&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/Sq1dEWflvLI/AAAAAAAAAko/KhJJy4oKUvA/s1600-h/clip_image034%5B4%5D.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="clip_image034" border="0" alt="clip_image034" src="http://lh3.ggpht.com/_vx0ImXMybTU/Sq1dFCGxEdI/AAAAAAAAAks/5teSnJCXFf8/clip_image034_thumb%5B1%5D.jpg?imgmax=800" width="346" height="118" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;h4&gt;Inserts in Excel&lt;/h4&gt;  &lt;p&gt;So far, we have only looked at updating values in existing rows. What about inserting new rows? The writeback Webmessages contain both Update and Insert templates. However, you may have noticed, if you looked at the query logs, that Insert statements have never been generated.&lt;/p&gt;  &lt;p&gt;To generate an insert, the OBIEE checks whether the first column in the edited row was originally null. If it was, it sets the action to insert rather than update. &lt;/p&gt;  &lt;p&gt;You can get this to work in Excel by adding a row into the named range containing the data that has the first column = Null. Since you may not want to have bogus values in the rest of the column, you could set all the columns to null. (Note – adding several null rows with the idea that you might want to insert multiple rows at once probably will not work. The reason is that the query to retrieve rows will be a SELECT DISTINCT, and so the result set displayed will contain only a single null row.)&lt;/p&gt;  &lt;p&gt;Filling in values in the null row and clicking the Writeback button in the table view will result in an insert statement. That's the good news. &lt;/p&gt;  &lt;p&gt;The bad news is that in this context, Excel will not perform the insert. The error message will read: &amp;quot;Cannot expand named range..&amp;quot;. So Inserts apparently won't work in Excel when the &amp;quot;table&amp;quot; is a named range. &lt;/p&gt;  &lt;h4&gt;Writeback in Oracle&lt;/h4&gt;  &lt;p&gt;I created a copy of the Excel data in the SH schema in Oracle 10g in a table named “WriteBack”. WriteBack had one row where all values are null.&lt;/p&gt;  &lt;p&gt;I set up a subject area and business model identical to the one used for showing writeback in Excel. I did this by copying the existing Excel Writeback business model, along with its presentation catalog, and then used the utility to replace the Excel Data table with the Oracle Writeback table (the utility is in the Tools|Utility menu list). &lt;/p&gt;  &lt;p&gt;I added this Webmessage to the writeback XML file:&lt;/p&gt;  &lt;p&gt;&amp;lt;WebMessage name= &amp;quot;OracleWriteBack&amp;quot;&amp;gt;    &lt;br /&gt;&amp;lt;XML&amp;gt;     &lt;br /&gt;&amp;lt;writeBack connectionPool=&amp;quot;orcl SH&amp;quot;&amp;gt;     &lt;br /&gt;&amp;lt;insert&amp;gt;INSERT INTO WRITEBACK VALUES(@{c0},@{c3},'@{c1}', TO_DATE('@{c2}', 'MM/DD/YYYY HH:MI:SS AM') )&amp;lt;/insert&amp;gt;     &lt;br /&gt;&amp;lt;update&amp;gt;UPDATE WRITEBACK SET &amp;quot;COL2&amp;quot;=@{c3},&amp;quot;COL3CHAR&amp;quot;='@{c1}',&amp;quot;COL4DATE&amp;quot;= TO_DATE('@{c2}', 'MM/DD/YYYY HH:MI:SS AM') WHERE &amp;quot;COL1&amp;quot;=@{c0}&amp;lt;/update&amp;gt;     &lt;br /&gt;&amp;lt;/writeBack&amp;gt;     &lt;br /&gt;&amp;lt;/XML&amp;gt;     &lt;br /&gt;&amp;lt;/WebMessage&amp;gt;&lt;/p&gt;  &lt;p&gt;Note that:&lt;/p&gt;  &lt;p&gt;1. The column names are upper case, since that is how they exist in the Oracle table and the template encloses them in quotation marks. &lt;/p&gt;  &lt;p&gt;2. The format mask for the TO_DATE function matches the date format displayed in the table. &lt;/p&gt;  &lt;p&gt;3. None of the single or double quotation marks are “smart quotes”. Smart quotes will cause the SQL to fail. &lt;/p&gt;  &lt;p&gt;I also turned off caching for the Oracle Writeback table in the metadata.&lt;/p&gt;  &lt;p&gt;To use a query that I had created for Excel, I first modified it by editing the XML in the Advanced tab, replacing&lt;b&gt; subjectArea=&amp;quot;ExcelWriteBack&amp;quot;&lt;/b&gt; with &lt;b&gt;subjectArea=&amp;quot;OracleWriteBack” &lt;/b&gt;. I then clicked the&lt;b&gt; Set XML &lt;/b&gt;button.&lt;b&gt; &lt;/b&gt;&lt;/p&gt;  &lt;p&gt;Note: Don’t make the subject area change in the Logical SQL window, or you will lose all the other table formatting and setup work you have already done.&lt;/p&gt;  &lt;p&gt;The next step was to edit the writeback properties of the table view in order to use the correct writeback template:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/Sq1dGI46xBI/AAAAAAAAAkw/CAe4jExDvXU/s1600-h/clip_image036%5B4%5D.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="clip_image036" border="0" alt="clip_image036" src="http://lh6.ggpht.com/_vx0ImXMybTU/Sq1dGrRiNWI/AAAAAAAAAk0/JVCWw_oRbS0/clip_image036_thumb%5B1%5D.jpg?imgmax=800" width="254" height="146" /&gt;&lt;/a&gt; &lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/Sq1dHHpzWPI/AAAAAAAAAk4/ZbV6vpuY0mA/s1600-h/clip_image038%5B4%5D.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="clip_image038" border="0" alt="clip_image038" src="http://lh5.ggpht.com/_vx0ImXMybTU/Sq1dHU6ahuI/AAAAAAAAAk8/O1Ps1LXWPx8/clip_image038_thumb%5B1%5D.jpg?imgmax=800" width="325" height="203" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;I saved the revised query, logged out of Answers, and restarted the presentation server. &lt;/p&gt;  &lt;p&gt;Remember to restart the presentation server any time you make a change to the &lt;b&gt;WriteBack.XML&lt;/b&gt; file.&lt;/p&gt;  &lt;p&gt;The table view for updating and inserting looked like this. Note the nulls in the last row. &lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/Sq1dH5mOAuI/AAAAAAAAAlA/VXvMc6FcbP4/s1600-h/clip_image040%5B4%5D.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="clip_image040" border="0" alt="clip_image040" src="http://lh3.ggpht.com/_vx0ImXMybTU/Sq1dIjfPEkI/AAAAAAAAAlE/Pc4ttI8iDMo/clip_image040_thumb%5B1%5D.jpg?imgmax=800" width="358" height="302" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Here is the same table view after making changes (“Jan” to “January” in the first row) and adding new data to the last row.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/Sq1dIzP6FoI/AAAAAAAAAlI/qOe-5RxWm8E/s1600-h/clip_image042%5B4%5D.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="clip_image042" border="0" alt="clip_image042" src="http://lh6.ggpht.com/_vx0ImXMybTU/Sq1dJjRe-vI/AAAAAAAAAlM/GIKcCJuUOqE/clip_image042_thumb%5B1%5D.jpg?imgmax=800" width="352" height="297" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Clicking the “Writeback” button generated the Update and Insert statements, followed by a Select statement that updated the table view.&lt;/p&gt;  &lt;pre&gt;UPDATE WRITEBACK SET &amp;quot;COL2&amp;quot;=10,&amp;quot;COL3CHAR&amp;quot;='January',&amp;quot;COL4DATE&amp;quot;= TO_DATE('1/31/2008 12:00:00 AM', 'MM/DD/YYYY HH:MI:SS AM') WHERE &amp;quot;COL1&amp;quot;=1&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;INSERT INTO WRITEBACK VALUES(11,110,'Nov',TO_DATE('11/30/2008 12:00:00 AM', 'MM/DD/YYYY HH:MI:SS AM'))&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;select T26869.COL1 as c1,&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160; T26869.COL3CHAR as c2,&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160; T26869.COL4DATE as c3,&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160; sum(T26869.COL2) as c4&lt;br /&gt;from &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160; WRITEBACK T26869&lt;br /&gt;group by T26869.COL1, T26869.COL3CHAR, T26869.COL4DATE&lt;br /&gt;order by c1, c2, c3 &lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/Sq1dKEdKqFI/AAAAAAAAAlQ/zJ6JoHBIpzI/s1600-h/clip_image044%5B4%5D.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="clip_image044" border="0" alt="clip_image044" src="http://lh4.ggpht.com/_vx0ImXMybTU/Sq1dKlQMggI/AAAAAAAAAlU/eq7p3oYSjHc/clip_image044_thumb%5B1%5D.jpg?imgmax=800" width="350" height="314" /&gt;&lt;/a&gt;&amp;#160;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;The full date and time do not need to be entered. Just entering the Date part alone will work. The last row shows the resulting data after adding another row where the input date was just 12/31/2008. &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/Sq1dLBW1SGI/AAAAAAAAAlY/FRXfw_1scBw/s1600-h/clip_image046%5B4%5D.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="clip_image046" border="0" alt="clip_image046" src="http://lh6.ggpht.com/_vx0ImXMybTU/Sq1dLkOCRDI/AAAAAAAAAlc/qAAGZ9LsWJk/clip_image046_thumb%5B1%5D.jpg?imgmax=800" width="355" height="311" /&gt;&lt;/a&gt;&amp;#160;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Tables with Keys&lt;/h4&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;If COL1 in the example above had been a key, NULLs could not exist in that column. In that case, how could you do an insert? &lt;br /&gt;  &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;  &lt;br /&gt;One way to solve it (there may be better ways but this is the only solution I came up with) is to create another data table, I called it WRITEBACKNULL, similar in structure to WRITEBACKKEYED but without a key. It contained one row where all the values were null. &lt;br /&gt;&lt;br /&gt;  &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;  &lt;br /&gt;I modified the business model to include WRITEBACKNULL as an additional source and set up fragmentation content so that queries would always use both sources (unless, for some reason, the user selected Dim.Col1 = 1000 or 1001, two non-existent values – but that could also be prevented by fragmenting on a logical column that wasn’t exposed in the presentation layer). &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/Sq1dMTJM8TI/AAAAAAAAAlg/knZ2BFk6Cfg/s1600-h/clip_image048%5B4%5D.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="clip_image048" border="0" alt="clip_image048" src="http://lh5.ggpht.com/_vx0ImXMybTU/Sq1dMgDhfGI/AAAAAAAAAlk/n1cxMPgO_gU/clip_image048_thumb%5B1%5D.jpg?imgmax=800" width="238" height="262" /&gt;&lt;/a&gt; &lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/Sq1dN-vLC7I/AAAAAAAAAl4/i0myOxdJIfs/s1600-h/clip_image050%5B1%5D.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="clip_image050" border="0" alt="clip_image050" src="http://lh5.ggpht.com/_vx0ImXMybTU/Sq1dOCenXDI/AAAAAAAAAl8/HwwokS2ZpEs/clip_image050_thumb.jpg?imgmax=800" width="319" height="154" /&gt;&lt;/a&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/Sq1dOt7OgTI/AAAAAAAAAmA/vetJAJS7leI/s1600-h/clip_image052%5B1%5D.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="clip_image052" border="0" alt="clip_image052" src="http://lh6.ggpht.com/_vx0ImXMybTU/Sq1dO3_97pI/AAAAAAAAAmE/GAWKA5VkcCw/clip_image052_thumb.jpg?imgmax=800" width="322" height="156" /&gt;&lt;/a&gt;&amp;#160;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;This will produce the null row needed in the table view to bring about an insert. &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;#160;&lt;/pre&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1481199104475905802-6872701039822982148?l=kpipartners.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/6872701039822982148'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/6872701039822982148'/><link rel='alternate' type='text/html' href='http://kpipartners.blogspot.com/2009/09/writeback-in-obiee.html' title='WriteBack in OBIEE'/><author><name>Kurt Wolff</name><uri>http://www.blogger.com/profile/07922051559586431987</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><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/_vx0ImXMybTU/Sq1cyq3ayhI/AAAAAAAAAiY/WjuLX28jklw/s72-c/image_thumb%5B5%5D.png?imgmax=800' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-1481199104475905802.post-1458327542410935732</id><published>2009-08-08T18:21:00.001-07:00</published><updated>2009-09-14T06:03:23.513-07:00</updated><title type='text'>When Fact Tables Do Not Join to All Dimension Tables</title><content type='html'>&lt;p&gt;When I read the questions people ask about data modeling on OBIEE forums, one that seems to come up frequently is what to do when you have fact tables that do not join to all dimension tables. &lt;/p&gt;  &lt;p&gt;This picture illustrates the problem. FactTable2 joins to two dimension tables, but FactTable1 only joins to only one.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/Sn4kl71lHTI/AAAAAAAAAhE/Ar-6hH8bP5U/s1600-h/image%5B9%5D.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_vx0ImXMybTU/Sn4kmprzGZI/AAAAAAAAAhI/bPhnNzqJPFU/image_thumb%5B5%5D.png?imgmax=800" width="292" height="153" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;The data for this example is very simple:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/Sn4km9bEDVI/AAAAAAAAAhM/ylf-W1w46Nc/s1600-h/image%5B8%5D.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_vx0ImXMybTU/Sn4kndullaI/AAAAAAAAAhQ/cAzKEkAoUFg/image_thumb%5B4%5D.png?imgmax=800" width="329" height="226" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Here’s what typically happens when the naive metadata designer is finished. The first query results look good.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/Sn4knn7qE9I/AAAAAAAAAhU/PGSuRG5kCCc/s1600-h/image%5B15%5D.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_vx0ImXMybTU/Sn4kn2ivZKI/AAAAAAAAAhY/GuGVvbe2HQc/image_thumb%5B9%5D.png?imgmax=800" width="125" height="95" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;But when the query contains a column from the second dimension table, suddenly the data that was there before disappears, and the first reaction is that something is wrong.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/Sn4koPrHIQI/AAAAAAAAAhc/Dtz2uYj5MFY/s1600-h/image%5B19%5D.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_vx0ImXMybTU/Sn4koa9Fg6I/AAAAAAAAAhg/ok1eP7zo5yg/image_thumb%5B11%5D.png?imgmax=800" width="173" height="140" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;From one perspective, these are correct result. The query asked for Fact1 aggregated by DimA and DimX. Since Fact1 does not join to Dim X, nulls are the correct answer.&lt;/p&gt;  &lt;p&gt;Looking at the SQL generated gives you an idea of how OBIEE navigates this query. It determines that the query wants Fact1 aggregated by DimADesc and DimXDesc. The only fact table that can aggregate by those two dimensions is Fact2. Therefore, the SQL it generates uses FactTable2 in the FROM clause, not FactTable1, &lt;em&gt;even though Fact1 does not map&lt;/em&gt; to any column in FactTable2. The BI Server is aware Fact1 does not map to FactTable2, so it returns Null (C3 in the outer query block below) as the value of Fact1.&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;select distinct D1.C1 as C1,     &lt;br /&gt;D1.C2 as C2,      &lt;br /&gt;&lt;font color="#54ab56"&gt;&lt;strong&gt;cast(null as &lt;i&gt;double&lt;/i&gt;&lt;i&gt;&lt;/i&gt; &lt;i&gt;precision&lt;/i&gt;&lt;i&gt;&lt;/i&gt;)&lt;/strong&gt;&lt;/font&gt; as C3      &lt;br /&gt;from       &lt;br /&gt;(select distinct T1416.DIMADESC as C1,      &lt;br /&gt;T1420.DIMXDESC as C2      &lt;br /&gt;from DIMX T1420,      &lt;br /&gt;DIMA T1416,      &lt;br /&gt;&lt;font color="#ff0000"&gt;FACTTABLE2&lt;/font&gt; T1429      &lt;br /&gt;where (T1416.DIMAKEY = T1429.DIMAFKEY      &lt;br /&gt;and T1420.DIMXKEY = T1429.DIMXFKEY)      &lt;br /&gt;) D1      &lt;br /&gt;order by C1, C2&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;However, what if Fact1 is a level-based measure, always calculated at the Grand Total level for DimX? Then the BI Server knows it does not have to aggregate by the attribute values of DimX and returns these results.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/Sn4ko_aQcBI/AAAAAAAAAhk/g2ON88y6pRQ/s1600-h/image%5B42%5D.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_vx0ImXMybTU/Sn4kpRK-hAI/AAAAAAAAAho/dK-_xHFv8nQ/image_thumb%5B22%5D.png?imgmax=800" width="167" height="139" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;In the SQL you can see that there is still a query block involving FactTable2. This query determines the values of&amp;#160; DimADesc and DimXDesc that will be in the results. The query to FactTable1 aggregates Fact1 by DimADesc. The two result sets are then fully outerjoined – even null values, if they are returned, will be joined, Null to Null.&lt;/p&gt;  &lt;pre&gt;&lt;font size="1"&gt;WITH SAWITH0 AS &lt;br /&gt;(SELECT DISTINCT &lt;br /&gt; T1420.DIMXDESC AS C1,&lt;br /&gt; T1416.DIMADESC AS C2&lt;br /&gt; FROM   DIMX T1420,&lt;br /&gt; DIMA T1416,&lt;br /&gt; &lt;font color="#ff0000"&gt;FACTTABLE2&lt;/font&gt; T1429&lt;br /&gt; WHERE  (T1416.DIMAKEY = T1429.DIMAFKEY&lt;br /&gt; AND T1420.DIMXKEY = T1429.DIMXFKEY)),&lt;br /&gt;SAWITH1 AS &lt;br /&gt;(SELECT   &lt;br /&gt;sum(T1424.FACT1) AS C1,&lt;br /&gt;T1416.DIMADESC   AS C2&lt;br /&gt;FROM     &lt;br /&gt;DIMA T1416,&lt;br /&gt;&lt;font color="#ff0000"&gt;FACTTABLE1&lt;/font&gt; T1424&lt;br /&gt;WHERE    (T1416.DIMAKEY = T1424.DIMAFKEY)&lt;br /&gt;GROUP BY T1416.DIMADESC)&lt;br /&gt;SELECT   DISTINCT&lt;br /&gt;CASE &lt;br /&gt;   WHEN SAWITH1.C2 IS NOT NULL THEN SAWITH1.C2&lt;br /&gt;   WHEN SAWITH0.C2 IS NOT NULL THEN SAWITH0.C2&lt;br /&gt;END AS C1,&lt;br /&gt;SAWITH0.C1 AS C2,&lt;br /&gt;SAWITH1.C1 AS C3&lt;br /&gt;FROM  &lt;br /&gt;SAWITH0&lt;br /&gt;FULL OUTER JOIN SAWITH1&lt;br /&gt;ON nvl(SAWITH0.C2,'q') = nvl(SAWITH1.C2,'q')&lt;br /&gt;AND nvl(SAWITH0.C2,'z') = nvl(SAWITH1.C2,'z')&lt;br /&gt;ORDER BY C1, C2&lt;/font&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;font face="Georgia"&gt;As the physical query shows, results will be determined by the foreign keys of DimX in FactTable2. If the rows where DimXFKey=2 are deleted, then the result rows where DimXDesc = Y will drop out.&lt;/font&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/Sn4kpnjt9yI/AAAAAAAAAhs/gV3MGfkVl6Y/s1600-h/image%5B37%5D.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_vx0ImXMybTU/Sn4kp8_aAcI/AAAAAAAAAhw/HwCP6fuNpbQ/image_thumb%5B19%5D.png?imgmax=800" width="176" height="85" /&gt;&lt;/a&gt; &lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;font face="Georgia"&gt;In this Business Model, both fact table sources (for FactTable1 and FactTable2) have an aggregation content of Detail for both dimensions (Detail is the default level when all logical levels are null in the logical table source). However, FactTable1 will not suffice as a source since it does not physically join to DimX.&lt;/font&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/Sn4kqB5SBvI/AAAAAAAAAh0/VI4RVHMgtnk/s1600-h/image%5B27%5D.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_vx0ImXMybTU/Sn4kq0xl28I/AAAAAAAAAh4/q-1mWaCJdLE/image_thumb%5B15%5D.png?imgmax=800" width="244" height="123" /&gt;&lt;/a&gt; &lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/Sn4krMoPLWI/AAAAAAAAAh8/NR0gSbD9q5U/s1600-h/image%5B30%5D.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_vx0ImXMybTU/Sn4krs5wDnI/AAAAAAAAAiA/fBzJQngPg4M/image_thumb%5B16%5D.png?imgmax=800" width="244" height="128" /&gt;&lt;/a&gt; &lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;font face="Georgia"&gt;Changing the aggregation content for FactTable1 does not alter the results or the SQL generated. (Note here that when one dimension has a level that is specified and the other dimension(s) are unspecified, the meaning the BI Server ascribes to unspecified is Grand Total).&lt;/font&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/Sn4kr8DQ3sI/AAAAAAAAAiE/c39KP56RT5Y/s1600-h/image%5B33%5D.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_vx0ImXMybTU/Sn4ksScs4II/AAAAAAAAAiI/EkazvvULdTQ/image_thumb%5B17%5D.png?imgmax=800" width="244" height="127" /&gt;&lt;/a&gt; &lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;An alternate approach is to physically join DimX to FactTable1 using a complex join having the join condition 1=1. The aggregation content for both logical fact table sources can be set at Detail so that any query containing columns from DimX can use FactTable1 as a source. The measure, Fact1, no longer has to be set to Grand Total level for DimX. Since FactTable2 will not be involved in the query, the foreign key values in FactTable2 will not matter. Now the results are back to what we saw in the first query.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/Sn4ksqy3p-I/AAAAAAAAAiM/P933M7-6X4o/s1600-h/image%5B41%5D.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_vx0ImXMybTU/Sn4ktFtkRtI/AAAAAAAAAiQ/CRAQDb-AC90/image_thumb%5B21%5D.png?imgmax=800" width="178" height="142" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;However, the SQL is quite different, and you can see why all the values of DimXDesc are returned.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;select T1416.DIMADESC as c1,&lt;br /&gt;     T1420.DIMXDESC as c2,&lt;br /&gt;     sum(T1659.FACT1) as c3&lt;br /&gt;from &lt;br /&gt;     DIMX T1420,&lt;br /&gt;     DIMA T1416,&lt;br /&gt;     &lt;font color="#ff0000"&gt;FACTTABLE1&lt;/font&gt; T1659 &lt;br /&gt;where  ( T1416.DIMAKEY = T1659.DIMAFKEY ) &lt;br /&gt;group by T1416.DIMADESC, T1420.DIMXDESC&lt;br /&gt;order by c1, c2&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;font face="Georgia"&gt;The important points to remember are &lt;br /&gt;that a physical fact table that does not join &lt;br /&gt;to a dimension table can be made to join with&lt;br /&gt; a complex join having the condition 1=1. &lt;br /&gt;The logical fact table source that contains this &lt;br /&gt;physical fact table can be set at the Detail level &lt;br /&gt;for the dimension that is joined like this. The facts &lt;br /&gt;that map to this logical table source should not &lt;br /&gt;be level-based.&lt;/font&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;#160;&lt;/pre&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1481199104475905802-1458327542410935732?l=kpipartners.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/1458327542410935732'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/1458327542410935732'/><link rel='alternate' type='text/html' href='http://kpipartners.blogspot.com/2009/08/when-fact-tables-do-not-join-to-all.html' title='When Fact Tables Do Not Join to All Dimension Tables'/><author><name>Kurt Wolff</name><uri>http://www.blogger.com/profile/07922051559586431987</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><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_vx0ImXMybTU/Sn4kmprzGZI/AAAAAAAAAhI/bPhnNzqJPFU/s72-c/image_thumb%5B5%5D.png?imgmax=800' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-1481199104475905802.post-512945711051777680</id><published>2009-07-31T10:39:00.001-07:00</published><updated>2009-07-31T13:36:35.186-07:00</updated><title type='text'>Setting Join Columns with Request Variables</title><content type='html'>&lt;p&gt;Here is a technique for enabling the user to set join columns with a dashboard prompt.&lt;/p&gt;  &lt;p&gt;An example of a case where this technique could be useful is when there are multiple date columns in a fact table. For example, a purchase order might have a purchase order date, a ship by date, and a cancel by date. The dashboard prompt would allow the user to set a date range in the period table and then determine which column in the fact table the period table would join to. By changing the join column the user changes the set of purchase orders that match the date prompt.&lt;/p&gt;  &lt;p&gt;The technique described here may not be the only way to accomplish this, and I’d welcome any other suggestions for better techniques. But here is one that works.&lt;/p&gt;  &lt;p&gt;The sample fact table I’ll use is a basic representation of a purchase order line.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/SnMsKTuNPWI/AAAAAAAAAfk/9byxyv0WymE/s1600-h/image%5B2%5D.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_vx0ImXMybTU/SnMsLMwjxDI/AAAAAAAAAfo/QC_yuZnAGs8/image_thumb.png?imgmax=800" width="186" height="191" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;The metadata defines a non-system session variable called JoinColumn which has an initialization block with the following SQL.&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;select 'CANCELBYDATE' from dual&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;The session variable also has a default value, which is the string ‘CANCELBYDATE’. (Of course, the default value could be different, and the init block could select a different value.)&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/SnMsMFtfmGI/AAAAAAAAAfs/DbLcOw0s63E/s1600-h/image%5B9%5D.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_vx0ImXMybTU/SnMsMi69lII/AAAAAAAAAfw/ETF_v1xegt4/image_thumb%5B3%5D.png?imgmax=800" width="364" height="107" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;The metadata allows any user to “set the value” – i.e. over write the session variable value by setting a request variable with the same name. (This overwriting occurs for queries that execute on a dashboard within the scope of the dashboard prompt).&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/SnMsNJd8xPI/AAAAAAAAAf0/mtBtJ0j9Q0A/s1600-h/image%5B13%5D.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_vx0ImXMybTU/SnMsN0qLjJI/AAAAAAAAAf4/f3BSb3vhXAo/image_thumb%5B5%5D.png?imgmax=800" width="361" height="234" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;In the physical layer, the table is defined by a Select statement that uses ValueOf(NQ_SESSION.JoinColumn) as one of the column names. ValueOf(NQ_SESSION.JoinColumn) can become PODATE, SHIPBYDATE, or CANCELBYDATE depending on the value of the session variable JoinColumn. The SQL gives the column the alias JCol.&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;Select ValueOf(NQ_SESSION.JoinColumn) as JCol      &lt;br /&gt;, ID       &lt;br /&gt;, Product       &lt;br /&gt;, Qty       &lt;br /&gt;from POS&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;For simplicity, the column UNITPRICE is ignored here.&amp;#160; The table looks like this in the physical layer. Note that the data type of JCol is Date.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/SnMsOYEN-JI/AAAAAAAAAf8/cTSwEemTt-U/s1600-h/image%5B19%5D.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_vx0ImXMybTU/SnMsO8PmQ1I/AAAAAAAAAgA/Q0zdVY-eMpI/image_thumb%5B9%5D.png?imgmax=800" width="162" height="97" /&gt;&lt;/a&gt; &lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/SnMsPKyBgqI/AAAAAAAAAgE/Xh-xX6Gekbs/s1600-h/image%5B23%5D.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_vx0ImXMybTU/SnMsP2uRUaI/AAAAAAAAAgI/TS6B3srGp8M/image_thumb%5B11%5D.png?imgmax=800" width="187" height="134" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;The physical join from the period table to the fact table is straightforward – JCol joins to the DATE_ column in TIME.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/SnMsQa6KK-I/AAAAAAAAAgM/m0ddiDWU2wQ/s1600-h/image%5B68%5D.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_vx0ImXMybTU/SnMsRC5Vm3I/AAAAAAAAAgQ/0W10vI8AXAw/image_thumb%5B38%5D.png?imgmax=800" width="337" height="261" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;The dashboard has a prompt that allows the user to set a date range in the TIME table as well as the value of the session variable JoinColumn.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/SnMsRV_1t2I/AAAAAAAAAgU/bc_92eRgPGg/s1600-h/image%5B33%5D.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_vx0ImXMybTU/SnMsR7QKziI/AAAAAAAAAgY/C1eTjhf6qcc/image_thumb%5B17%5D.png?imgmax=800" width="368" height="61" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;To show the set up of the dashboard prompt more clearly, here are two pictures of it. The request variable name must match the name of the session variable (case sensitive).&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/SnMsSeAStbI/AAAAAAAAAgc/JpX-K6VpBAc/s1600-h/image%5B48%5D.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_vx0ImXMybTU/SnMsTB6oS8I/AAAAAAAAAgg/Vzj7HbocmHg/image_thumb%5B26%5D.png?imgmax=800" width="402" height="126" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/SnMsTa2hRTI/AAAAAAAAAgk/mM5F-kmfqHY/s1600-h/image%5B53%5D.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_vx0ImXMybTU/SnMsT6GBnTI/AAAAAAAAAgo/bphqnAInkX0/image_thumb%5B29%5D.png?imgmax=800" width="401" height="182" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;The values of the join column show up in a drop-down list using this SQL:&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;SELECT case when 1=0 then TIME.&amp;quot;MONNAME&amp;quot; else 'PODATE' end FROM SetJoin&amp;#160; &lt;br /&gt;union all       &lt;br /&gt;SELECT case when 1=0 then TIME.&amp;quot;MONNAME&amp;quot; else 'SHIPBYDATE' end FROM SetJoin&amp;#160; &lt;br /&gt;union all       &lt;br /&gt;SELECT case when 1=0 then TIME.&amp;quot;MONNAME&amp;quot; else 'CANCELBYDATE'end&amp;#160; FROM SetJoin&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;The default value is set to a specific value, CANCELBYDATE.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/SnMsUaUtJ3I/AAAAAAAAAgs/NWtWOIXuXEY/s1600-h/image%5B43%5D.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_vx0ImXMybTU/SnMsU0Sd6II/AAAAAAAAAgw/NzfodZhHzKQ/image_thumb%5B23%5D.png?imgmax=800" width="203" height="136" /&gt;&lt;/a&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;As the user makes different selections of the join column, the SQL generated matches those selections. &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/SnMsVRGTzWI/AAAAAAAAAg0/dnD-wrRCyOo/s1600-h/image%5B58%5D.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_vx0ImXMybTU/SnMsV-onhyI/AAAAAAAAAg4/P1Q2xEt3P5o/image_thumb%5B32%5D.png?imgmax=800" width="388" height="257" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;select T463.DATE_ as C1    &lt;br /&gt;,&lt;b&gt;Sum&lt;/b&gt;&lt;b&gt;&lt;/b&gt;(T946.QTY) as C2     &lt;br /&gt;from time T463     &lt;br /&gt;,(select &lt;strong&gt;&lt;font color="#ff0000"&gt;CANCELBYDATE&lt;/font&gt;&lt;/strong&gt; as JCOL     &lt;br /&gt;,id     &lt;br /&gt;,PRODUCT     &lt;br /&gt;,QTY     &lt;br /&gt;from POS) T946     &lt;br /&gt;where (T463.DATE_ = T946.JCOL     &lt;br /&gt;and T463.DATE_ between &lt;b&gt;To_date&lt;/b&gt;&lt;b&gt;&lt;/b&gt;('2009-07-31','YYYY-MM-DD') and &lt;b&gt;To_date&lt;/b&gt;&lt;b&gt;&lt;/b&gt;('2009-09-30','YYYY-MM-DD')     &lt;br /&gt;and T946.JCOL between &lt;b&gt;To_date&lt;/b&gt;&lt;b&gt;&lt;/b&gt;('2009-07-31','YYYY-MM-DD') and &lt;b&gt;To_date&lt;/b&gt;&lt;b&gt;&lt;/b&gt;('2009-09-30','YYYY-MM-DD'))     &lt;br /&gt;group by T463.DATE_     &lt;br /&gt;order by C1&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/SnMsWGtgkUI/AAAAAAAAAg8/h363IkZv59k/s1600-h/image%5B63%5D.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_vx0ImXMybTU/SnMsWmTTrPI/AAAAAAAAAhA/okuC0gkaz2I/image_thumb%5B35%5D.png?imgmax=800" width="419" height="318" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;select T463.DATE_ as C1      &lt;br /&gt;,&lt;b&gt;Sum&lt;/b&gt;&lt;b&gt;&lt;/b&gt;(T946.QTY) as C2       &lt;br /&gt;from time T463       &lt;br /&gt;,(select &lt;font color="#ff0000"&gt;&lt;strong&gt;SHIPBYDATE&lt;/strong&gt;&lt;/font&gt; as JCOL       &lt;br /&gt;,id       &lt;br /&gt;,PRODUCT       &lt;br /&gt;,QTY       &lt;br /&gt;from POS) T946       &lt;br /&gt;where (T463.DATE_ = T946.JCOL       &lt;br /&gt;and T463.DATE_ between &lt;b&gt;To_date&lt;/b&gt;&lt;b&gt;&lt;/b&gt;('2009-07-31','YYYY-MM-DD') and &lt;b&gt;To_date&lt;/b&gt;&lt;b&gt;&lt;/b&gt;('2009-09-30','YYYY-MM-DD')       &lt;br /&gt;and T946.JCOL between &lt;b&gt;To_date&lt;/b&gt;&lt;b&gt;&lt;/b&gt;('2009-07-31','YYYY-MM-DD') and &lt;b&gt;To_date&lt;/b&gt;&lt;b&gt;&lt;/b&gt;('2009-09-30','YYYY-MM-DD'))       &lt;br /&gt;group by T463.DATE_       &lt;br /&gt;order by C1&lt;/font&gt;&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1481199104475905802-512945711051777680?l=kpipartners.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/512945711051777680'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/512945711051777680'/><link rel='alternate' type='text/html' href='http://kpipartners.blogspot.com/2009/07/setting-join-columns-with-request.html' title='Setting Join Columns with Request Variables'/><author><name>Kurt Wolff</name><uri>http://www.blogger.com/profile/07922051559586431987</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><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/_vx0ImXMybTU/SnMsLMwjxDI/AAAAAAAAAfo/QC_yuZnAGs8/s72-c/image_thumb.png?imgmax=800' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-1481199104475905802.post-1774266020406246593</id><published>2009-07-27T17:52:00.001-07:00</published><updated>2009-07-27T17:52:01.407-07:00</updated><title type='text'>Need Selected Date + 30 days data</title><content type='html'>&lt;p&gt;Someone asked a question on a “Siebel Analytics” email group a few days ago that I thought was worth discussing briefly here. The question posed was how to put a date prompt on a dashboard and return data for that date and the next 30 days. The solution could not involve presentation or report variables, since these were introduced only in a later version of Siebel Analytics that the person who posed the question did not have.&lt;/p&gt;  &lt;p&gt;When you have a problem like this, it usually helps to think about the SQL the BI Server would need to generate. In this case, it would include a WHERE clause with a BETWEEN predicate. That is, the normal key/foreign key equijoin between the fact table and the period table would have to become a join of the fact table&amp;#160; foreign key between a date in the period table and that date plus 30 days. This is a good example or a key principle: whenever a join condition needs to change, you should think about creating a new alias that uses that join condition.&lt;/p&gt;  &lt;p&gt;Here’s an example of the usual equijoin of the period table (“TIME”) to the fact table (“TIMECOMPAREFACTS”).&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/Sm5Lp6MaJoI/AAAAAAAAAfM/W1TqrRu1uX4/s1600-h/image%5B4%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_vx0ImXMybTU/Sm5LqXFPUDI/AAAAAAAAAfQ/POKiqOem21A/image_thumb%5B2%5D.png?imgmax=800" width="317" height="345" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Using an alias of the fact table (“TimeSpanFacts”) and a“complex” instead of a physical foreign key join, the join expression changes.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/Sm5Lqy1zkyI/AAAAAAAAAfU/sMMv2a4vRXo/s1600-h/image%5B14%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_vx0ImXMybTU/Sm5LreO0yRI/AAAAAAAAAfY/cA-T5QpCiwo/image_thumb%5B8%5D.png?imgmax=800" width="322" height="316" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Now the user picks a date with the dashboard prompt and clicks Go.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/Sm5LroIiikI/AAAAAAAAAfc/z-bkld22_7c/s1600-h/image%5B19%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_vx0ImXMybTU/Sm5LsG9MGjI/AAAAAAAAAfg/iPlHP74o9E8/image_thumb%5B11%5D.png?imgmax=800" width="253" height="218" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;The BI Server generates the SQL needed as specified in the metadata.&lt;/p&gt;  &lt;p&gt;select T463.DATE_ as C1   &lt;br /&gt;,&lt;b&gt;Sum&lt;/b&gt;&lt;b&gt;&lt;/b&gt;(T884.QS) as C2    &lt;br /&gt;,&lt;b&gt;Sum&lt;/b&gt;&lt;b&gt;&lt;/b&gt;(T884.DOLLARS) as C3    &lt;br /&gt;from TIME&amp;#160; T463    &lt;br /&gt;, TIMECOMPAREFACTS T884 &lt;i&gt;/* TimeSpanFacts */&lt;/i&gt;&lt;i&gt;&lt;/i&gt;    &lt;br /&gt;where     &lt;br /&gt;(    &lt;br /&gt;T463.DATE_ = &lt;b&gt;To_date&lt;/b&gt;&lt;b&gt;&lt;/b&gt;('2009-04-01','YYYY-MM-DD')    &lt;br /&gt;and     &lt;br /&gt;&lt;font color="#ff0000"&gt;T884.DATEKEY between T463.DATE_ and (T463.DATE_ + 30)     &lt;br /&gt;&lt;/font&gt;)    &lt;br /&gt;group by T463.DATE_    &lt;br /&gt;order by C1&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1481199104475905802-1774266020406246593?l=kpipartners.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/1774266020406246593'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/1774266020406246593'/><link rel='alternate' type='text/html' href='http://kpipartners.blogspot.com/2009/07/need-selected-date-30-days-data.html' title='Need Selected Date + 30 days data'/><author><name>Kurt Wolff</name><uri>http://www.blogger.com/profile/07922051559586431987</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><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/_vx0ImXMybTU/Sm5LqXFPUDI/AAAAAAAAAfQ/POKiqOem21A/s72-c/image_thumb%5B2%5D.png?imgmax=800' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-1481199104475905802.post-6821609075362288727</id><published>2009-07-19T17:47:00.001-07:00</published><updated>2009-07-19T17:47:36.283-07:00</updated><title type='text'>Comparing Arbitrary Time Periods</title><content type='html'>&lt;p&gt;The Ago() “time series function” can be used to show data for a&amp;#160; previous time period, as long as the previous time period corresponds to a level that has been defined in the period hierarchy. A typical period hierarchy containing day, month, quarter, and year levels would allow you to use the Ago function to construct measures showing data for day ago, month ago, quarter ago, year ago (or N days ago, N months ago, etc.). &lt;/p&gt;  &lt;p&gt;However, sometimes there is the need to compare facts in arbitrary time periods. For example, if an analyst wanted to compare sales from March 19 to March 23 2008 with sales during the period March 30 to April 12 2009 (not entirely fanciful – these were the two weeks before Easter in the United States in 2008 and 2009), the Ago function is not a solution.&lt;/p&gt;  &lt;p&gt;In addition, use of the Ago function requires you to define separate measures for all levels, Ns, and base measure combinations that you might want to use. So if you had the base measures Quantity Sold and Amount Sold you could end up with separate measures for Quantity Sold Week Ago, Quantity Sold 2 Weeks Ago, Quantity Sold 3 Weeks Ago, …, Quantity Sold Month Ago, Quantity Sold 2 Months Ago, …, Quantity Sold Year Ago, Quantity Sold 2 Years Ago, …&amp;#160; and the same for Amount Sold and every other base measure. In addition, you might define variances or % Change measures around each of these combinations – i.e. Quantity Sold % Change vs. Week Ago, vs. 2 Weeks Ago, vs. 3 Weeks Ago, ….&lt;/p&gt;  &lt;p&gt;This post is about designing a dashboard page that provides an easy way for users to select an arbitrary time period and an arbitrary comparison time period and have the measures aggregated over those two time periods along with variance or % Change calculations.&amp;#160; The basic functionality would look like this to the user. The user sets values in a dashboard prompt for the base period (for example,&amp;#160; 2/7/2009 through 2/28/2009). Then he/she selects the comparison period (for example, 1/3/2009 through 1/24/2009. Then he/she clicks the Go button in the prompt.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/SmO2s6tGcJI/AAAAAAAAAck/E9frVVVSucE/s1600-h/image%5B4%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_vx0ImXMybTU/SmO2xSlAZqI/AAAAAAAAAco/25odBe2vc9I/image_thumb%5B2%5D.png?imgmax=800" width="389" height="163" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Or the user could compare the period from 3/1/2009 through 3/18/2009 with the period from 2/8/2009 through 2/28/2009.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/SmO22_zNngI/AAAAAAAAAcs/QZUPwoVw_AM/s1600-h/image%5B10%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_vx0ImXMybTU/SmO23fL12vI/AAAAAAAAAcw/4ZZXcQaAfRQ/image_thumb%5B6%5D.png?imgmax=800" width="391" height="170" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;There are several approaches you might think of to provide a working solution. I’ll discuss two here, but there might be others. &lt;/p&gt;  &lt;p&gt;The first involves creating two separate queries that have two different time constraints, unioning the results together, and then combining results using a pivot table. &lt;/p&gt;  &lt;p&gt;There are some downsides to using a union, because with a union you give up navigation, you give up column selectors, you give up calculating a % Change, and you will have some&amp;#160; deficiencies with charts. The upside is it needs only a simple change in the metadata. &lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Using A Union (“Combining Similar Queries”)&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;This approach seems to work best if you create a second presentation column to enable setting two period constraints in a dashboard prompt. &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/SmO23ltPgXI/AAAAAAAAAc0/1fd0fqbWurM/s1600-h/image%5B15%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_vx0ImXMybTU/SmO24GZFOiI/AAAAAAAAAc4/NgRRVCIVUbQ/image_thumb%5B9%5D.png?imgmax=800" width="320" height="201" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Build a dashboard prompt having both “Date” and “Comparison Date” using the “is between” Operator with both.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/SmO24rkRM3I/AAAAAAAAAc8/e9gmTo0Yr6k/s1600-h/image%5B20%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_vx0ImXMybTU/SmO25DQ4k_I/AAAAAAAAAdA/OGJd-i_dTjk/image_thumb%5B12%5D.png?imgmax=800" width="379" height="86" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Construct the first query using “Date is prompted”. &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/SmO25vOlX8I/AAAAAAAAAdE/aTsCexGgJM8/s1600-h/image%5B30%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_vx0ImXMybTU/SmO26BmAEwI/AAAAAAAAAdI/fpeeMIdiDW4/image_thumb%5B18%5D.png?imgmax=800" width="379" height="163" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;The column formulas are    &lt;br /&gt;&lt;font face="Courier New"&gt;Geography.State,      &lt;br /&gt;Facts.”Quantity Sold”,       &lt;br /&gt;0,&amp;#160; &lt;br /&gt;Facts.”Quantity Sold”.&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;Click “Combine Similar Queries” and construct the second query(you can take a shortcut here by copying the original query). In the second query, change the filter to use the Comparison Date column.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/SmO27O_-KRI/AAAAAAAAAdM/bwBRlItVc-4/s1600-h/image%5B36%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_vx0ImXMybTU/SmO279g9elI/AAAAAAAAAdQ/TEwxUPk5F5s/image_thumb%5B22%5D.png?imgmax=800" width="357" height="309" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;The column formulas in the second query are &lt;font face="Courier New"&gt;Geography.State,      &lt;br /&gt;0,       &lt;br /&gt;Facts.”Quantity Sold”,       &lt;br /&gt;-Facts.”Quantity Sold”.&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;When you run the query (no constraints having yet been set on dates), you end up with something like this:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/SmO28I6vZ2I/AAAAAAAAAdU/w45BxVwApek/s1600-h/image%5B52%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_vx0ImXMybTU/SmO282CJysI/AAAAAAAAAdY/8hdfT3iOmBU/image_thumb%5B34%5D.png?imgmax=800" width="340" height="287" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;Then use the pivot table view to sum up the columns, grouping by the common non-aggregatable column values. Be sure to set the aggregation rule for each fact column (aggregation = sum).&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/SmO2-KLlhvI/AAAAAAAAAdc/wQRUmKFp6bM/s1600-h/image%5B51%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_vx0ImXMybTU/SmO2__a5_hI/AAAAAAAAAdg/LGa9bWURrTo/image_thumb%5B33%5D.png?imgmax=800" width="343" height="238" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;If you want to chart the results, you may need to use the pivot table chart view.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/SmO3B0EfFyI/AAAAAAAAAdk/ekn8p6j0sgI/s1600-h/image%5B57%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_vx0ImXMybTU/SmO3DICR5JI/AAAAAAAAAdo/EqnOvQRMDSM/image_thumb%5B37%5D.png?imgmax=800" width="354" height="160" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;This is how it looks on the dashboard after date values have been entered.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/SmO3FEbNpFI/AAAAAAAAAds/2i_pd6d7hVU/s1600-h/image%5B117%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_vx0ImXMybTU/SmO3FmJNSUI/AAAAAAAAAdw/aCXyzQspIRY/image_thumb%5B71%5D.png?imgmax=800" width="362" height="185" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Second Period Dimension and Fact Table Alias&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Another approach is to introduce a second period dimension and an alias of the fact table. This requires more metadata work but you end up getting back all the features you forego with unions. Unions (and the other set operation queries using intersect and minus operators) were supposed to become “first class query citizens” several years ago, but have not made it (yet). In the meantime, we need to find other approaches. The steps are outlined here.&lt;/p&gt;  &lt;p&gt;Create new aliases of the period table and fact table.&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/SmO3F6WO--I/AAAAAAAAAd0/KFT0U3ot9nw/s1600-h/image%5B69%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_vx0ImXMybTU/SmO3GCklGiI/AAAAAAAAAd4/9eNsL46kQi4/image_thumb%5B43%5D.png?imgmax=800" width="122" height="41" /&gt;&lt;/a&gt;     &lt;br /&gt;Create physical joins. The table TIMECOMPAREPRODUCTS and TIMECOMPAREGEOG were the existing product and geography tables.    &lt;br /&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/SmO3Gp1AafI/AAAAAAAAAd8/qPaZ58YTVGo/s1600-h/image%5B78%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_vx0ImXMybTU/SmO3HNBQJvI/AAAAAAAAAeA/UIV0PI4l0QQ/image_thumb%5B48%5D.png?imgmax=800" width="355" height="184" /&gt;&lt;/a&gt;     &lt;br /&gt;Add the second period table to the business model, including the logical joins.    &lt;br /&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/SmO3HfRw4DI/AAAAAAAAAeE/ln7BrmpXbRw/s1600-h/image%5B123%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_vx0ImXMybTU/SmO3H74aGyI/AAAAAAAAAeI/xDwMx3H_PfI/image_thumb%5B75%5D.png?imgmax=800" width="269" height="127" /&gt;&lt;/a&gt;&amp;#160; &lt;br /&gt;Add a second period dimension to the business model.    &lt;br /&gt;&amp;#160;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/SmO3I4SCIOI/AAAAAAAAAeM/DrI8qi_gT7M/s1600-h/image%5B122%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_vx0ImXMybTU/SmO3J_pP4YI/AAAAAAAAAeQ/wqVd3ADJPGM/image_thumb%5B74%5D.png?imgmax=800" width="382" height="114" /&gt;&lt;/a&gt;     &lt;br /&gt;Add a new logical fact table source.    &lt;br /&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/SmO3K2tlrHI/AAAAAAAAAeU/A9DHAVU9UPs/s1600-h/image%5B86%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_vx0ImXMybTU/SmO3RWwUIKI/AAAAAAAAAeY/yYShW-hIenc/image_thumb%5B52%5D.png?imgmax=800" width="215" height="67" /&gt;&lt;/a&gt;&amp;#160; &lt;br /&gt;Create new comparison base measures and map them into comparison logical fact table source.    &lt;br /&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/SmO3RkJZVFI/AAAAAAAAAec/rYE4DSbOztc/s1600-h/image%5B90%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_vx0ImXMybTU/SmO3TSyLZrI/AAAAAAAAAeg/lQZdwcSHMa8/image_thumb%5B54%5D.png?imgmax=800" width="204" height="129" /&gt;&lt;/a&gt;     &lt;br /&gt;Because the new period dimension does not relate to all facts (nor, now, does the old period dimension) , set the level attributes of the base and comparison measures to the Total level for the dimension that does not relate to them.    &lt;br /&gt;    &lt;br /&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/SmO3VVlNWyI/AAAAAAAAAek/hhJGG-VfwJc/s1600-h/image%5B99%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_vx0ImXMybTU/SmO3kymS51I/AAAAAAAAAeo/3PCBRPvxov8/image_thumb%5B59%5D.png?imgmax=800" width="242" height="144" /&gt;&lt;/a&gt; &lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/SmO3l0hFvpI/AAAAAAAAAes/X7i5l1GKEHI/s1600-h/image%5B95%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_vx0ImXMybTU/SmO3mllQdqI/AAAAAAAAAew/F0OfdTgR18U/image_thumb%5B57%5D.png?imgmax=800" width="242" height="144" /&gt;&lt;/a&gt;&amp;#160; &lt;br /&gt;More than likely, you will want to create variance and % Change measures.    &lt;br /&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/SmO3pLxSAaI/AAAAAAAAAe0/cWETbVGmprg/s1600-h/image%5B104%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_vx0ImXMybTU/SmO3pnT08EI/AAAAAAAAAe4/3PfOyl9letg/image_thumb%5B62%5D.png?imgmax=800" width="195" height="175" /&gt;&lt;/a&gt;     &lt;br /&gt;Add the new objects to the presentation layer.    &lt;br /&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/SmO3qv1d0DI/AAAAAAAAAe8/8qANFD2Y2vM/s1600-h/image%5B109%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_vx0ImXMybTU/SmO3rYrw3TI/AAAAAAAAAfA/5t8Ibrh_D78/image_thumb%5B65%5D.png?imgmax=800" width="286" height="387" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;You can now create dashboard pages that support arbitrary time comparisons along with regular navigation and drilling. Combining this with column selectors, the dashboard page gives users a lot of latitude to do the analyses they need without using Answers.   &lt;br /&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/SmO3xhe3IjI/AAAAAAAAAfE/XsYDeaCKr7w/s1600-h/image%5B116%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_vx0ImXMybTU/SmO31vIt71I/AAAAAAAAAfI/a8HsBMzzJq0/image_thumb%5B70%5D.png?imgmax=800" width="407" height="188" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Put something like this together and, if you are curious,&amp;#160; see how simple the resulting SQL is compared to what happens with the Ago function!&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1481199104475905802-6821609075362288727?l=kpipartners.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/6821609075362288727'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/6821609075362288727'/><link rel='alternate' type='text/html' href='http://kpipartners.blogspot.com/2009/07/comparing-arbitrary-time-periods.html' title='Comparing Arbitrary Time Periods'/><author><name>Kurt Wolff</name><uri>http://www.blogger.com/profile/07922051559586431987</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><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/_vx0ImXMybTU/SmO2xSlAZqI/AAAAAAAAAco/25odBe2vc9I/s72-c/image_thumb%5B2%5D.png?imgmax=800' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-1481199104475905802.post-4946317141934779247</id><published>2009-07-11T15:02:00.001-07:00</published><updated>2009-07-11T15:07:21.230-07:00</updated><title type='text'>Visually Presenting Data in Tables and Pivot Tables</title><content type='html'>&lt;p&gt;There are times when you might want to visually present data directly in tables or pivot tables rather than create a chart view. For example, you may have too many values to show in a chart. Or the number of values returned by the query might vary, making the size of the chart sometimes too small to accommodate them all, sometimes too large. Or you may want to visually scroll up and down a table to compare values and would like a visual representation right there in the table.&lt;/p&gt;  &lt;p&gt;This posting will present two ways to do that.&lt;/p&gt;  &lt;p&gt;Here’s an example of data that we would like to represent visually.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/SlkLsQVxn9I/AAAAAAAAAbQ/-ZdtOLHsu_w/s1600-h/image%5B8%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_vx0ImXMybTU/SlkLs7tPGtI/AAAAAAAAAbU/FReVq1wapTE/image_thumb%5B6%5D.png?imgmax=800" width="122" height="507" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;A quick and easy visualization can be done using the text Repeat function.&amp;#160; The formula here will repeat the upper case “I” character as many times as there are thousands in the Amount Sold column. &lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;repeat('I', case when &amp;quot;Sell Through Facts&amp;quot;.&amp;quot;Amount Sold&amp;quot; is null then 0 else cast(round(&amp;quot;Sell Through Facts&amp;quot;.&amp;quot;Amount Sold&amp;quot;/1000,0) as integer) end )&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/SlkLtGbmSxI/AAAAAAAAAbY/dzJBK1uUPRk/s1600-h/image%5B13%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_vx0ImXMybTU/SlkLttFGQbI/AAAAAAAAAbc/SPKH9Fdel6c/image_thumb%5B9%5D.png?imgmax=800" width="395" height="113" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;You can easily change the format of the resulting text to get the look you want. For example, here is the&amp;#160; format that produces the visualization below.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/SlkLt5KYxbI/AAAAAAAAAbg/4VjD5Ni0UGE/s1600-h/image%5B17%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_vx0ImXMybTU/SlkLwdr3xfI/AAAAAAAAAbk/L0efHSFuFg8/image_thumb%5B11%5D.png?imgmax=800" width="410" height="160" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/SlkLw-grkoI/AAAAAAAAAbo/vCZZxtj81FU/s1600-h/image%5B22%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_vx0ImXMybTU/SlkLxTZGSlI/AAAAAAAAAbw/VrePLBAP1ks/image_thumb%5B14%5D.png?imgmax=800" width="228" height="541" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;The formula casts to integer in order to avoid the following error message.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/SlkLx9xgdjI/AAAAAAAAAb0/V3zVkmCAnZQ/s1600-h/image%5B43%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_vx0ImXMybTU/SlkLyIUijII/AAAAAAAAAb4/DLALvNtvbyo/image_thumb%5B27%5D.png?imgmax=800" width="401" height="166" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;A second way to do this is to use the Google chart API to draw a horizontal bar chart. Google’s API can produce a variety of chart types including bar, horizontal bar, pie, and line. Using a line chart in the right place can be especially powerful.&lt;/p&gt;  &lt;p&gt;The simplest way to use Google, in my opinion, is first of all to normalize the data as a percentage of the maximum value in the result set. &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/SlkLyttCFcI/AAAAAAAAAb8/9KUw4CJO2Fc/s1600-h/image%5B27%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_vx0ImXMybTU/SlkLy3-uWLI/AAAAAAAAAcA/N4c8lAAithk/image_thumb%5B17%5D.png?imgmax=800" width="398" height="117" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;The second step is to create another column using a formula that will generate a URL that conforms to the parameters in the Google chart API. The first part of the formula, represented here in yellow, is text that sets chart type, size, and color. The part after that is the data. The Cast converts the normalized data to text and Replace removes any “.” characters.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/SlkLzQonoJI/AAAAAAAAAcE/4S36CV8PWDk/s1600-h/image%5B32%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_vx0ImXMybTU/SlkLzxNH7CI/AAAAAAAAAcI/ug-w_k17-oM/image_thumb%5B20%5D.png?imgmax=800" width="407" height="71" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Here’s the text of the formula.&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier"&gt;'http://chart.apis.google.com/chart?cht=bhs&amp;amp;chs=100x20&amp;amp;chco=4d89f9&amp;amp;chd=t:'||replace( trim(cast(ifnull(100*&amp;quot;Sell Through Facts&amp;quot;.&amp;quot;Amount Sold&amp;quot;/max(&amp;quot;Sell Through Facts&amp;quot;.&amp;quot;Amount Sold&amp;quot;),0) as char(3))),'.','')&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;Set the Data Format to treat text as an Image URL.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/SlkL0K0_58I/AAAAAAAAAcM/LoCOArJ2YUw/s1600-h/image%5B48%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_vx0ImXMybTU/SlkL0TwhL-I/AAAAAAAAAcQ/-YA6DEtE5XI/image_thumb%5B30%5D.png?imgmax=800" width="355" height="107" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt; The result looks like this.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/SlkL0x_p8yI/AAAAAAAAAcU/l-hRJYmS9Vg/s1600-h/image%5B38%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_vx0ImXMybTU/SlkL1QhWFbI/AAAAAAAAAcY/OuWms0XMcL8/image_thumb%5B24%5D.png?imgmax=800" width="238" height="593" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;Google charts are described at &lt;a title="http://code.google.com/apis/chart/" href="http://code.google.com/apis/chart/"&gt;http://code.google.com/apis/chart/&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;There you can find all the details you need to change colors, sizes, and chart types. Google’s API integrates very nicely with OBIEE.&amp;#160; Here’s the same data, represented as vertical bar charts in a pivot table. You can see that this technique gives you a way to visualize trends very easily.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/SlkL1xUzdRI/AAAAAAAAAcc/NpW0I2SguT0/s1600-h/image%5B61%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_vx0ImXMybTU/SlkL2JN21nI/AAAAAAAAAcg/nvLUszcX0CI/image_thumb%5B39%5D.png?imgmax=800" width="400" height="68" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1481199104475905802-4946317141934779247?l=kpipartners.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/4946317141934779247'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/4946317141934779247'/><link rel='alternate' type='text/html' href='http://kpipartners.blogspot.com/2009/07/visually-presenting-data-in-tables-and.html' title='Visually Presenting Data in Tables and Pivot Tables'/><author><name>Kurt Wolff</name><uri>http://www.blogger.com/profile/07922051559586431987</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><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_vx0ImXMybTU/SlkLs7tPGtI/AAAAAAAAAbU/FReVq1wapTE/s72-c/image_thumb%5B6%5D.png?imgmax=800' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-1481199104475905802.post-6749339304830812228</id><published>2009-07-10T16:07:00.001-07:00</published><updated>2009-07-10T16:41:02.603-07:00</updated><title type='text'>Groups, Webgroups, and Delivers</title><content type='html'>&lt;p&gt;I thought it would be worth exploring the interrelated topics of repository groups, web groups, system session variables,&amp;#160; Delivers, the SA System subject area, and My Account.&amp;#160; &lt;/p&gt;  &lt;p&gt;First comment: I am not sure that anyone knows any more, if they ever did, how all these things are actually supposed to interrelate. The best we can do is try things, see what happens, and learn from experiment and experience.&lt;/p&gt;  &lt;p&gt;To that end, I created the table Users1 with information about&amp;#160; four users, including their BI server (RPD) group and presentation server (Web) group memberships. My practice is to not use the same names for BI server groups and presentation server groups.&amp;#160; (Things get confusing rapidly if you do that, in my opinion). This is the content of the table:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/SlfJDGqqERI/AAAAAAAAAWg/q8Hwt85s_tk/s1600-h/image%5B4%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_vx0ImXMybTU/SlfJDYxi-mI/AAAAAAAAAWk/hWzMjBV_t3E/image_thumb%5B2%5D.png?imgmax=800" width="347" height="113" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;User authentication occurs via an initialization block called “Authenticate”. The SQL of the Authenticate init block is:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;Select     &lt;br /&gt;Logon      &lt;br /&gt;, Displayname      &lt;br /&gt;, RPDGroup      &lt;br /&gt;, Webgroup      &lt;br /&gt;from      &lt;br /&gt;Users1      &lt;br /&gt;where      &lt;br /&gt;upper(Logon) = upper(':USER')      &lt;br /&gt;NQS_PASSWORD_CLAUSE(and password =':PASSWORD')NQS_PASSWORD_CLAUSE&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;The session variables populated by this init block are&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/SlfJD5RwR8I/AAAAAAAAAWo/UGf2kMvFSeQ/s1600-h/image%5B8%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_vx0ImXMybTU/SlfJEGORKXI/AAAAAAAAAWs/y7_r881_2vk/image_thumb%5B4%5D.png?imgmax=800" width="143" height="80" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;There are a couple of basic things to know here.&amp;#160; :USER is the user ID entered by the user in the logon screen (or passed to the BI Server by some external authentication system, such as Oracle SSO). :PASSWORD is the password entered by the user. Both of these have to be typed in upper case in the SQL.&amp;#160; &lt;strong&gt;Note:&lt;/strong&gt; Enclosing the constraint on password with NQS_PASSWORD_CLAUSE as shown in the SQL above is required with Delivers. If your system uses SSO to authenticate users, then remove the password constraint from the init block completely.&lt;/p&gt;  &lt;p&gt;The session variables to be populated by the init block have to be in the same order as the columns in the SQL. Variable population depends on the order only, not in matching the names of the columns to the names of the variables. &lt;strong&gt;NOTE&lt;/strong&gt; : because order of the variables is the only thing that matters, beware that sometimes, spontaneously it seems, the order of variables can change. If this occurs, break up your init block into multiple init blocks until the order of variables remains stable. &lt;/p&gt;  &lt;p&gt;The variables names used here are special system session variables. Note: The variable “GROUP”&amp;#160; is singular, but you can assign a user to multiple groups in the init block. “WEBGROUPS” is plural. If you enter “WEBGROUP” (singular) the init block will not assign users to web groups. &lt;/p&gt;  &lt;p&gt;In the Users1 table, users are being assigned to multiple groups and web groups with group and webgroup names separated by semi-colons. (Another way to do this would be to populate GROUP or WEBGROUPS variables using&amp;#160; row-wise initialization. As far as I can tell, both methods work the same in all respects.)&lt;/p&gt;  &lt;p&gt;To illustrate how this works, let’s use an example RPD having five presentation catalogs. &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/SlfJEuA8BII/AAAAAAAAAWw/-z-HBu3COSM/s1600-h/image%5B17%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_vx0ImXMybTU/SlfJE6vqLYI/AAAAAAAAAW0/gQYtX9kfSNs/image_thumb%5B9%5D.png?imgmax=800" width="132" height="119" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;BI Server group “GroupA” has access to Retail Data A. GroupB has access to Retail Data B, etc.&amp;#160; The table User1 puts A in GroupA and GroupB. Here’s what A sees in Answers.&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/SlfJFRszTHI/AAAAAAAAAW4/Mnjrd9clKU8/s1600-h/image%5B19%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_vx0ImXMybTU/SlfJFrFyY1I/AAAAAAAAAW8/29NhEJZnX90/image_thumb%5B11%5D.png?imgmax=800" width="174" height="148" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;If A goes to My Account in the web UI, it shows that A is a member of the web group&amp;#160; WebA.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/SlfJF26kSLI/AAAAAAAAAXA/o15_y84RqO0/s1600-h/image%5B22%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_vx0ImXMybTU/SlfJGf4ZGaI/AAAAAAAAAXE/NGRHzgQCeMo/image_thumb%5B12%5D.png?imgmax=800" width="244" height="87" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;User B logs in and also sees Retail Data A and Retail Data B, since B is also a member of GroupA and GroupB. My Account shows B belongs to web groups WebA and WebB.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/SlfJHJAFyfI/AAAAAAAAAXI/yxwpc-NIdQc/s1600-h/image%5B188%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_vx0ImXMybTU/SlfJHZAyXHI/AAAAAAAAAXM/x_Da-J0ZIpM/image_thumb%5B112%5D.png?imgmax=800" width="242" height="104" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;User C logs in, sees three subject areas, and is a member of three web groups. &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/SlfJHw_TJLI/AAAAAAAAAXQ/pdDcy80RkYI/s1600-h/image%5B26%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_vx0ImXMybTU/SlfJIf3JgnI/AAAAAAAAAXU/gRfexGW7yps/image_thumb%5B14%5D.png?imgmax=800" width="157" height="155" /&gt;&lt;/a&gt;&amp;#160; &lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/SlfJIiaSS5I/AAAAAAAAAXY/cLflAUy213o/s1600-h/image%5B30%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_vx0ImXMybTU/SlfJIwZm1ZI/AAAAAAAAAXc/o9_SVxJRfGw/image_thumb%5B16%5D.png?imgmax=800" width="242" height="110" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;At this point in time, users A, B, and C have logged in but user D has not. Now the administrator user logs in and examines the list of users and groups in the Administration UI in the web. Here’s what the administrator sees: users A, B, and C are listed as users.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/SlfJJHMoj9I/AAAAAAAAAXg/2bQzjuTwVag/s1600-h/image%5B42%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_vx0ImXMybTU/SlfJJp0k7_I/AAAAAAAAAXk/6QY-i6Jkcfs/image_thumb%5B24%5D.png?imgmax=800" width="211" height="240" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;If Administrator looks at who is a member of WebA, however, no one is listed.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/SlfJKNDPBEI/AAAAAAAAAXo/qGNOC-1LxkA/s1600-h/image%5B46%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_vx0ImXMybTU/SlfJKmRr05I/AAAAAAAAAXs/X_nKWMrEv-4/image_thumb%5B26%5D.png?imgmax=800" width="240" height="209" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;The user’s name persists once the user logs in,&amp;#160; but the web group membership appears not to. &lt;/p&gt;  &lt;p&gt;In Delivers, you can name a web group as a recipient of an alert. What happens when the alert is addressed to members of WebA at this point? Since WebA has no members, there are no recipients. Users A, B, and C will not receive the alert. If&amp;#160; the administrator tries to add A, B, and C to WebA, he will see this.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/SlfJLjCGqYI/AAAAAAAAAXw/O46mLJrloMo/s1600-h/image%5B52%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_vx0ImXMybTU/SlfJMIt7_AI/AAAAAAAAAX0/ICkt_tF0LLE/image_thumb%5B30%5D.png?imgmax=800" width="235" height="240" /&gt;&lt;/a&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;In this context, the web catalog appears to remember that A, B, and C have been assigned to WebA, and it is not possible for the Administrator to add them.&amp;#160; Now suppose User D logs in.&amp;#160; He now becomes a listed Catalog User and a member of WebD, as shown on his My Account page.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/SlfJMlp-6aI/AAAAAAAAAX4/gn6W3KuyrWU/s1600-h/image%5B58%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_vx0ImXMybTU/SlfJM-MNA8I/AAAAAAAAAX8/TlsZ6g8ZiZs/image_thumb%5B34%5D.png?imgmax=800" width="242" height="96" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;Now if Administrator logs in and attempts to add users to WebA, this is what he sees.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/SlfJNHVjSVI/AAAAAAAAAYA/kQ9A_Y286bE/s1600-h/image%5B62%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_vx0ImXMybTU/SlfJN7OD2zI/AAAAAAAAAYE/Agsc8m93-4Y/image_thumb%5B36%5D.png?imgmax=800" width="197" height="240" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;D, who has not been a member of WebA, can be added, but not A, B, or C. Suppose the administrator adds D to WebA.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/SlfJOAnEs-I/AAAAAAAAAYI/DxaAs4bOoLI/s1600-h/image%5B66%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_vx0ImXMybTU/SlfJOmfpRYI/AAAAAAAAAYM/Ml746d65deU/image_thumb%5B38%5D.png?imgmax=800" width="199" height="240" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;When D logs in and goes to the My Account page, it shows his group membership includes WebA and WebD.&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/SlfJPJlK29I/AAAAAAAAAYQ/sS0-yzqDXtY/s1600-h/image%5B79%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_vx0ImXMybTU/SlfJPbP_xeI/AAAAAAAAAYU/0G2VHBc1Jek/image_thumb%5B45%5D.png?imgmax=800" width="242" height="103" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;Now suppose the Administrator creates an iBot using the subject area Retail Data A with Personalized data visibility and chooses the group WebA as a recipient. &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/SlfJPzVplYI/AAAAAAAAAYY/q3NiGA3odTc/s1600-h/image%5B91%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_vx0ImXMybTU/SlfJQEfHUeI/AAAAAAAAAYc/9pGSddOodzQ/image_thumb%5B51%5D.png?imgmax=800" width="240" height="108" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/SlfJQrNph8I/AAAAAAAAAYg/aPVapMqo3Tc/s1600-h/image%5B75%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_vx0ImXMybTU/SlfJQ2sIGJI/AAAAAAAAAYk/Sfq8-t4M52k/image_thumb%5B43%5D.png?imgmax=800" width="240" height="175" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;When User D logs in he will not see the alert. &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/SlfJRCrbuVI/AAAAAAAAAYo/UnDhAvcgm2w/s1600-h/image%5B96%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_vx0ImXMybTU/SlfJRofNXEI/AAAAAAAAAYs/JCyAiPzl0pA/image_thumb%5B54%5D.png?imgmax=800" width="320" height="66" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;The reason is that only members of the repository group GroupA have access to the subject area Retail Data A. When the Administrator modifies the data visibility setting of the alert so that it is Not Personalized and is run as the Administrator user, User D will receive the alert.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/SlfJR0uwPNI/AAAAAAAAAYw/YZAP9M846ZE/s1600-h/image%5B102%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_vx0ImXMybTU/SlfJSUKCEEI/AAAAAAAAAY0/H4IhbBfP9vc/image_thumb%5B58%5D.png?imgmax=800" width="333" height="82" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/SlfJSrAqptI/AAAAAAAAAY4/ZPvhDn8NzZg/s1600-h/image%5B97%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_vx0ImXMybTU/SlfJTXIS-qI/AAAAAAAAAY8/XEkPqqE1hZA/image_thumb%5B55%5D.png?imgmax=800" width="327" height="76" /&gt;&lt;/a&gt;     &lt;br /&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/SlfJTrwuL9I/AAAAAAAAAZA/w2gR6brLuAU/s1600-h/image%5B107%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_vx0ImXMybTU/SlfJUFxhiSI/AAAAAAAAAZE/wYZFND01qns/image_thumb%5B61%5D.png?imgmax=800" width="334" height="159" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;User A is a member of GroupA, therefore has access to the subject area Retail Data A. When A logs in, he doesn’t see the alert notification, since A is not an explicit member of Web Group A – he is a member by virtue of the value of the WEBGROUPS session variable.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/SlfJUc5vnTI/AAAAAAAAAZI/pJRZfH1ul7g/s1600-h/image%5B112%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_vx0ImXMybTU/SlfJU6njZHI/AAAAAAAAAZM/m1b7kzwkUCI/image_thumb%5B64%5D.png?imgmax=800" width="351" height="34" /&gt;&lt;/a&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;However, when he clicks on More Products|Delivers|Show iBots Acting On My Behalf, the alert is listed. It’s listed as acting on his behalf but, apparently, he won’t receive the contents of the iBot!&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/SlfJVBesdzI/AAAAAAAAAZQ/o-Fa9yv3kec/s1600-h/image%5B117%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_vx0ImXMybTU/SlfJVujjrLI/AAAAAAAAAZU/mOCXyLLJl84/image_thumb%5B67%5D.png?imgmax=800" width="389" height="75" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;By the way, clicking on the iBot link, “QS Alert”, only shows the settings of the iBot, not the contents.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/SlfJV0n7EUI/AAAAAAAAAZY/7iGzRt80SOU/s1600-h/image%5B122%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_vx0ImXMybTU/SlfJWYeg8AI/AAAAAAAAAZc/m18YFCaKDEo/image_thumb%5B70%5D.png?imgmax=800" width="363" height="241" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Even if A is logged on at the time the iBot is running, he will not see the iBot alert.&lt;/p&gt;  &lt;p&gt;What about the “SA System” subject area? The basic purpose of SA System is to set the delivery profiles of users, providing default delivery settings for users who have not entered settings in My Account. (SA System can also overwrite user settings in My Account if a parameter to do that is set in instanceconfig.xml). &lt;/p&gt;  &lt;p&gt;In this example, SA System columns are mapped&amp;#160; to columns in the Users1 table or to string constants in the metadata for logical columns Email, Email Type, Email Priority, Cell Phone, and Cell Phone Priority.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/SlfJW3Yy_vI/AAAAAAAAAZg/vAwU00TqUCk/s1600-h/image%5B134%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_vx0ImXMybTU/SlfJXbHZ2-I/AAAAAAAAAZk/BMazHZp1s7c/image_thumb%5B78%5D.png?imgmax=800" width="389" height="93" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;When UserC, who has not set up and devices or delivery profiles, views My Account settings, he sees a System Email device and a System Profile.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/SlfJXpcLamI/AAAAAAAAAZo/rqxD_9iG1WQ/s1600-h/image%5B141%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_vx0ImXMybTU/SlfJYBmCPoI/AAAAAAAAAZs/UTB3Nm52d3c/image_thumb%5B83%5D.png?imgmax=800" width="379" height="341" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;These conform to the settings from SA System.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/SlfJYslg1oI/AAAAAAAAAZw/5UjbmblLhzU/s1600-h/image%5B146%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_vx0ImXMybTU/SlfJZRgGNNI/AAAAAAAAAZ0/JsnDf2BBMK8/image_thumb%5B86%5D.png?imgmax=800" width="382" height="141" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/SlfJZmQS64I/AAAAAAAAAZ4/c0x84fywW_c/s1600-h/image%5B154%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_vx0ImXMybTU/SlfJaPLqomI/AAAAAAAAAZ8/1n6viC7Ul10/image_thumb%5B92%5D.png?imgmax=800" width="352" height="210" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;When A views My Account, he sees the settings from SA System, but they are not active, since A has defined his own settings.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/SlfJaj0svkI/AAAAAAAAAaA/VDIY43_9soc/s1600-h/image%5B159%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_vx0ImXMybTU/SlfJc2exeoI/AAAAAAAAAaE/UiirjnT4zsA/image_thumb%5B95%5D.png?imgmax=800" width="347" height="265" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;There is a way to get Delivers to deliver content to all members of WebA, however. Set up another request (it could use the SA System subject area, but it doesn’t have to) that will return the members of WebA. Then make this request the conditional request for the iBot. &lt;/p&gt;  &lt;p&gt;The following query was saved as “Deliver to WebA”.&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/SlfJdEW_HVI/AAAAAAAAAaI/rscVMqi9wTU/s1600-h/image%5B168%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_vx0ImXMybTU/SlfJdiZMYmI/AAAAAAAAAaM/SDCVSzN8UCY/image_thumb%5B100%5D.png?imgmax=800" width="239" height="197" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;This query was specified as the conditional request in the iBot.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/SlfJdwT-dyI/AAAAAAAAAaQ/esZdKQxlfVc/s1600-h/image%5B179%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_vx0ImXMybTU/SlfJeSDuaBI/AAAAAAAAAaU/gFNrKMSKT_c/image_thumb%5B107%5D.png?imgmax=800" width="306" height="212" /&gt;&lt;/a&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;The Recipients tab was set to determine recipients from the conditional request.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/SlfJfmq4AjI/AAAAAAAAAaY/6NU7XZAnWpg/s1600-h/image%5B178%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_vx0ImXMybTU/SlfJgCctK7I/AAAAAAAAAac/qF7hqbj_Sng/image_thumb%5B106%5D.png?imgmax=800" width="301" height="131" /&gt;&lt;/a&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;The Delivery Content tab is where the query that generated content was specified.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/SlfJgT27ZSI/AAAAAAAAAag/6AfgaU2hnlQ/s1600-h/image%5B184%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_vx0ImXMybTU/SlfJg945BRI/AAAAAAAAAak/xYQ3Zelus5E/image_thumb%5B110%5D.png?imgmax=800" width="314" height="129" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;The iBot was fired off. When user A logged in, this time he saw the alert.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/SlfJhkuH-XI/AAAAAAAAAao/btXvEHFmZW0/s1600-h/image%5B164%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_vx0ImXMybTU/SlfJiJwjf1I/AAAAAAAAAas/lINAgQxj-4o/image_thumb%5B98%5D.png?imgmax=800" width="353" height="46" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;However, the shortcoming here is that you cannot deliver personalized results. Suppose there are filters on Groups A, B, and C when querying Retail Data A. GroupA sees only Product A, GroupB sees only ProductB, and GroupC sees only ProductC.&lt;/p&gt;  &lt;p&gt;UserA and UserB are members of both GroupA and GroupB. Their results include both Product A and Product B.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/SlfJiZ4D7WI/AAAAAAAAAaw/ZnAZTs-CYYQ/s1600-h/image%5B201%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_vx0ImXMybTU/SlfJixsCjqI/AAAAAAAAAa0/3IJ4vQnMg2Y/image_thumb%5B117%5D.png?imgmax=800" width="95" height="62" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Members of Group C (which User C belongs to)&amp;#160; see only Product C.   &lt;br /&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/SlfJjKc8l3I/AAAAAAAAAa4/Qa9leGMBZC8/s1600-h/image%5B202%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_vx0ImXMybTU/SlfJjYGaDVI/AAAAAAAAAa8/OQfUMQ4wzBc/image_thumb%5B118%5D.png?imgmax=800" width="96" height="47" /&gt;&lt;/a&gt;. &lt;/p&gt;  &lt;p&gt;An iBot that has a conditional request (“Deliver to WebA”), specific named recipients (Users A, B, and C), and personalized data visibility does not get delivered.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/SlfJjjMjVPI/AAAAAAAAAbA/bcE1lwdJa90/s1600-h/image%5B207%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_vx0ImXMybTU/SlfJkH0pfcI/AAAAAAAAAbE/1sxtBwgdTyk/image_thumb%5B121%5D.png?imgmax=800" width="429" height="252" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;After modifying this iBot so that the conditional request is the same as the content (the query “QS” is used for both), Users A, B, and C receive the iBot with personalized content.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/SlfJkrSOpfI/AAAAAAAAAbI/jwedR14fSvs/s1600-h/image%5B219%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_vx0ImXMybTU/SlfJlBKmkII/AAAAAAAAAbM/UTKnk9nv_d8/image_thumb%5B131%5D.png?imgmax=800" width="445" height="268" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;So where are we? It would be nice to be able to assign users to web groups using session variables, to send iBots to those users by specifying groups as recipients, and to personalize the content of the iBots. It seems as though there ought to be a way to do all this, but the pieces in OBIEE don’t really seem to fit together at this time. Depending on what you want to achieve, you may need to use a variety of methods to configure iBots and deliver alerts.&lt;/p&gt;  &lt;p&gt;.   &lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1481199104475905802-6749339304830812228?l=kpipartners.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/6749339304830812228'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/6749339304830812228'/><link rel='alternate' type='text/html' href='http://kpipartners.blogspot.com/2009/07/groups-webgroups-and-delivers.html' title='Groups, Webgroups, and Delivers'/><author><name>Kurt Wolff</name><uri>http://www.blogger.com/profile/07922051559586431987</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><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/_vx0ImXMybTU/SlfJDYxi-mI/AAAAAAAAAWk/hWzMjBV_t3E/s72-c/image_thumb%5B2%5D.png?imgmax=800' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-1481199104475905802.post-3370050674589441892</id><published>2009-06-19T20:13:00.001-07:00</published><updated>2009-06-19T20:23:43.827-07:00</updated><title type='text'>Using WITH in Physical Tables (“Opaque Views”)</title><content type='html'>&lt;p&gt;Common practice is to use derived tables (sometimes referred to as “inline views”) when creating a physical layer “Select” table. &lt;/p&gt;  &lt;p&gt;What about using the SQL-99 WITH clause instead, which does not begin with SELECT? After all, WITH clauses have some advantages: the SQL can be easier to read and execute faster. &lt;/p&gt;  &lt;p&gt;My experience is that a WITH clause will work in an opaque view as long as the database supports WITH, of course. Oracle started supporting it in version 9i. However, there is an important caveat: the SQL will error out if the opaque view itself is embedded in another WITH clause generated by the BI server.&lt;/p&gt;  &lt;p&gt;To repeat: if the BI server generates SQL that is of the following form, the Oracle database will execute it.    &lt;br /&gt;    &lt;br /&gt;SELECT …    &lt;br /&gt;FROM    &lt;br /&gt;… ,    &lt;br /&gt;(    &lt;br /&gt;/*-------Physical Layer Table Using With -----*/     &lt;br /&gt;) T    &lt;br /&gt;WHERE …    &lt;br /&gt;GROUP BY …&lt;/p&gt;  &lt;p&gt;However this form of SQL will fail.&lt;/p&gt;  &lt;p&gt;&lt;font face="Courier New"&gt;&lt;font face="Georgia"&gt;WITH        &lt;br /&gt;SAWITH0 AS&lt;/font&gt;      &lt;br /&gt;(&lt;/font&gt;SELECT …    &lt;br /&gt;FROM    &lt;br /&gt;… ,    &lt;br /&gt;(    &lt;br /&gt;/*-------Physical Layer Table Using With -----*/     &lt;br /&gt;) T    &lt;br /&gt;WHERE …    &lt;br /&gt;GROUP BY …&lt;/p&gt;  &lt;p&gt;You can control whether the BI server generates SQL using the first or second form by changing the database features in the metadata.&lt;/p&gt;  &lt;p&gt;I ran the same logical query under different sets of database features using Oracle XE. The query had measures from two logical fact table sources, one of which was an opaque view that used WITH. Both LTSs had to be queried and the results outer joined together.&lt;/p&gt;  &lt;p&gt;The default features for Oracle produced the second form of SQL and this error message:&lt;/p&gt;  &lt;pre&gt;Oracle Error code: 32034, message: ORA-32034: unsupported use of WITH clause&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/SjxTbG7xVmI/AAAAAAAAAWA/-eEONQ38NcI/s1600-h/image%5B4%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_vx0ImXMybTU/SjxTbZ1R5yI/AAAAAAAAAWE/4H-4xiAF8xc/image_thumb%5B2%5D.png?imgmax=800" width="387" height="53" /&gt;&lt;/a&gt;&amp;#160;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Turning on PERF_PREFER_MINIMAL_WITH_USAGE resulted in a different error, an internal Oracle error (an Oracle bug there are patches for). If patched, the SQL should run (but I did not test this) since the SQL generated is of the first form.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/SjxT0C-e6JI/AAAAAAAAAWI/9Iv1f_Ou7xc/s1600-h/image%5B10%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_vx0ImXMybTU/SjxT0tUQbXI/AAAAAAAAAWM/SOGy69nURVc/image_thumb%5B6%5D.png?imgmax=800" width="380" height="57" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;The error I saw in this case was&lt;br /&gt;  &lt;br /&gt;&lt;font face="Courier"&gt;ORA-00942: table or view does not exist&lt;/font&gt;&lt;br /&gt;&lt;br /&gt;  &lt;br /&gt;This is an error message that is symptomatic of the Oracle bug.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Turning on the PERF_PREFER_INTERNAL_STITCH_JOIN fixed that problem and the SQL generated, which was of the first form, ran successfully.&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/SjxT1spm7CI/AAAAAAAAAWQ/CICA1wZ3ZCk/s1600-h/image%5B15%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_vx0ImXMybTU/SjxT2Ao2o0I/AAAAAAAAAWU/NdiVuanPUbc/image_thumb%5B9%5D.png?imgmax=800" width="379" height="52" /&gt;&lt;/a&gt; Turning off WITH_CLAUSE_SUPPORTED, logically the safest way to prevent the second form of SQL from being generated, also worked, as expected. &lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/SjxT22bhWtI/AAAAAAAAAWY/ezWPDu9DsFY/s1600-h/image%5B20%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_vx0ImXMybTU/SjxT33HnWVI/AAAAAAAAAWc/pDnSooK4WZ0/image_thumb%5B12%5D.png?imgmax=800" width="382" height="54" /&gt;&lt;/a&gt; &lt;strong&gt;Conclusion&lt;/strong&gt;: You can use WITH in opaque views. In the admin tool, you can right click on the opaque view and update row count or view data to make sure that the opaque view is syntactically correct.&lt;br /&gt;&lt;br /&gt;  &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;  &lt;br /&gt;However, you will have to adjust database features so the BI server will not generate its own SQL WITHs. Probably the safest coure is to turn off WITH_CLAUSE_SUPPORTED. This may be too drastic for some people’s tastes – though, to be honest, the WITH SQL that the BI server generates tends to be about as “baroque” as derived table SQL. So you’re not gaining a lot of readability by having the BI server use WITH. &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Even if you do not want to use WITHs in opaque views in the final metadata version, it can still be helpful to use them during metadata development. If you are writing an opaque view that is complex when using derived tables, writing it using WITH can be easier. Test that SQL verify that the logic is correct, and then translate it back to use derived tables in your finished product.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1481199104475905802-3370050674589441892?l=kpipartners.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/3370050674589441892'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/3370050674589441892'/><link rel='alternate' type='text/html' href='http://kpipartners.blogspot.com/2009/06/sql-in-physical-tables-opaque-views.html' title='Using WITH in Physical Tables (“Opaque Views”)'/><author><name>Kurt Wolff</name><uri>http://www.blogger.com/profile/07922051559586431987</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><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/_vx0ImXMybTU/SjxTbZ1R5yI/AAAAAAAAAWE/4H-4xiAF8xc/s72-c/image_thumb%5B2%5D.png?imgmax=800' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-1481199104475905802.post-4750796282146028847</id><published>2009-06-08T00:11:00.001-07:00</published><updated>2009-06-08T00:11:38.962-07:00</updated><title type='text'>Multi-Select with an All Values Choice, and Fragments</title><content type='html'>&lt;p&gt;Here’s a not uncommon scenario. You are designing a dashboard page with a report such as sales by customer. To show sales by customer, you design a report containing the CustomerName column so that facts are grouped by customer. You provide a multi-select prompt to allow the user to select which customer data to view. But you also want to provide the ability to view the data for all customers as a total, without grouping by customer.&lt;/p&gt;  &lt;p&gt;For example, consider the following sales data for a group of retailers and suppliers.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/Siy5dWxnevI/AAAAAAAAAUQ/ysjkOG1tHJc/s1600-h/image%5B2%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_vx0ImXMybTU/Siy5dgIo9QI/AAAAAAAAAUU/g46EXPa6hKM/image_thumb.png?imgmax=800" width="244" height="161" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Each supplier wants to view the data by customer, but also wants to see the data for all customers as a total.&lt;/p&gt;  &lt;p&gt;Mike wants to see data for A and C as separate items, but also has the need to see what sales are for the total of A + C. Ned wants to see data for C, D, and the total of C + D. Oscar wants to see the data for A, B, C, or D as well as the total of all four.&lt;/p&gt;  &lt;p&gt;In other words, Mike wants a multi-select prompt for CustomerName that offers him three choices: A, C, and All Customers -- something that looks like this:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/Siy5eNAfOzI/AAAAAAAAAUY/KOhgoBCrGm8/s1600-h/image%5B7%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_vx0ImXMybTU/Siy5etKvUgI/AAAAAAAAAUc/udFFdbvIvaU/image_thumb%5B3%5D.png?imgmax=800" width="377" height="178" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;This would allow Mike to see a report like this &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/Siy5fBEsJ5I/AAAAAAAAAUg/h-H5qjxByE4/s1600-h/image%5B10%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_vx0ImXMybTU/Siy5fSWItnI/AAAAAAAAAUk/8cXq3ytdXl4/image_thumb%5B4%5D.png?imgmax=800" width="163" height="70" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;or this&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/Siy5fnhv20I/AAAAAAAAAUo/xgnErCNYcmY/s1600-h/image%5B13%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_vx0ImXMybTU/Siy5gNgha9I/AAAAAAAAAUs/sULT_F5Bj-0/image_thumb%5B5%5D.png?imgmax=800" width="160" height="48" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;using the same prompt and the same report.&lt;/p&gt;  &lt;p&gt;This can be done by creating multiple sources for the CustomerName logical column – one source for individual customers and another source for All Customers – and then combining both sources (unioning all) as separate fragments.&amp;#160; The SQL you need to have the BI server generate should be of the form:&lt;/p&gt;  &lt;p&gt;select D0.C1 as C1   &lt;br /&gt;,&lt;b&gt;sum&lt;/b&gt;&lt;b&gt;&lt;/b&gt;(D0.C2) as C2    &lt;br /&gt;from (    &lt;br /&gt;(select     &lt;br /&gt;T3476.CUSTOMERNAME as C1    &lt;br /&gt;,T3484.AMOUNT as C2    &lt;br /&gt;from …)    &lt;br /&gt;union all    &lt;br /&gt;(select    &lt;br /&gt; '**All Customers' as C1    &lt;br /&gt;,T3484.AMOUNT as C2    &lt;br /&gt;from …)     &lt;br /&gt;)D0    &lt;br /&gt;group by D0.C1&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;To get this union all to happen, you need to use the Fragmentation content feature of the logical table source object. In this case you could create two sources as fragments.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/Siy5gQywuFI/AAAAAAAAAUw/hB_S7Fvf7WY/s1600-h/image%5B22%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_vx0ImXMybTU/Siy5gyM-I2I/AAAAAAAAAU0/IE6aao7KahQ/image_thumb%5B10%5D.png?imgmax=800" width="378" height="128" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/Siy5hRFw4sI/AAAAAAAAAU4/s8bktUXNq6Q/s1600-h/image%5B23%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_vx0ImXMybTU/Siy5hiEPG0I/AAAAAAAAAU8/YR8_u4-F4pc/image_thumb%5B11%5D.png?imgmax=800" width="388" height="140" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;To get the union all to work, you not only need to have checks in the checkboxes, you also need to define different fragmentation content. It does not really matter what the fragmentation content is! In this example, there is a logical column, “Customer Selection”, that is used to define the fragmentation content. This logical column does not even have to be exposed in the presentation layer. Its sole function is to tell the admin tool that these two fragments have different content and therefore need to be combined to get all the values for the CustomerName logical column.&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;In the business model I generated for this topic, I have a table called “SupplierCustomer” that lists all the Customers doing business with each supplier. This table is included in each logical table source. In the source for the individual customer names, it joins to the Customers table.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/Siy5iMOZ1WI/AAAAAAAAAVA/nnY9F5EjTU8/s1600-h/image%5B33%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_vx0ImXMybTU/Siy5iV0D02I/AAAAAAAAAVE/uWGxbU5RBDM/image_thumb%5B17%5D.png?imgmax=800" width="339" height="244" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Note the logical column to physical column mapping. &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/Siy5ix-ytLI/AAAAAAAAAVI/OILzi2vlS1I/s1600-h/image%5B32%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_vx0ImXMybTU/Siy5jsjpRoI/AAAAAAAAAVM/APr_Hq_vlCU/image_thumb%5B16%5D.png?imgmax=800" width="349" height="74" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;In the other logical table source, the only table is the SupplierCustomer table (or, in this case, an alias of it).&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/Siy5kVXHZFI/AAAAAAAAAVQ/YysJTT5-00A/s1600-h/image%5B37%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_vx0ImXMybTU/Siy5k7h0OjI/AAAAAAAAAVU/LlZkqktbuvw/image_thumb%5B19%5D.png?imgmax=800" width="276" height="106" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;The logical column CustomerName maps to the value   &lt;br /&gt; ‘**All Customers’. (The double asterisks help to sort this value first in the list of customers).&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/Siy5lPMlVnI/AAAAAAAAAVY/LP00T3wqaJM/s1600-h/image%5B42%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_vx0ImXMybTU/Siy5lq7EJnI/AAAAAAAAAVc/-RqVgmQR2XY/image_thumb%5B22%5D.png?imgmax=800" width="351" height="76" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;This example also uses an init block to set the value of a SupplierID session variable when the user (i.e. the supplier) logs in. This session variable is also used in the WHERE clause of each LTS.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/Siy5mVsOyXI/AAAAAAAAAVg/I5q1GTX3fJM/s1600-h/image%5B46%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_vx0ImXMybTU/Siy5mxBDwxI/AAAAAAAAAVk/WzwzRb61WH0/image_thumb%5B24%5D.png?imgmax=800" width="351" height="60" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;This technique works reasonably well, but there is one big problem: it fails when a column based on a time series function (Ago or ToDate) is used. For example, this query succeeded.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/Siy5n4t7AmI/AAAAAAAAAVo/QY904Vzxfek/s1600-h/image%5B49%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_vx0ImXMybTU/Siy5oMiQpjI/AAAAAAAAAVs/iNFWLPbhLK0/image_thumb%5B25%5D.png?imgmax=800" width="244" height="62" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;When logged in as Ned, it produced these results:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/Siy5oSjzvVI/AAAAAAAAAVw/bqL39Bu7Jck/s1600-h/image%5B52%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_vx0ImXMybTU/Siy5paF48aI/AAAAAAAAAV0/avgSQ1QALtQ/image_thumb%5B26%5D.png?imgmax=800" width="236" height="170" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;However, when the column Month Ago Amount, defined using the Ago function, was added&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/Siy5p9dTFpI/AAAAAAAAAV4/QyPu4pRvzt8/s1600-h/image%5B56%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_vx0ImXMybTU/Siy5qMu6i5I/AAAAAAAAAV8/1TQmDBIcRts/image_thumb%5B28%5D.png?imgmax=800" width="314" height="64" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;an error occurred.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;[nQSError: 22049] Function AGO may not be used in a query that refers partitioned (fragmented) Logical Table Sources.&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;I was surprised when I saw this error. When the time series functions were introduced (and sadly, in my opinion, the Time Series Wizard was deleted) they imposed many restrictions in the metadata. In recent releases, some of these restrictions have been eliminated,&amp;#160; but not this one. It is too bad, since fragments in general are a valuable feature and have always been a core feature of OBIEE. So you have two choices: abandon fragments entirely or abandon time series functions and do time series the old fashioned way.&amp;#160; But if you choose the second path, you have to manually perform each step to set up Period Ago comparison measures, since you no longer have the time series wizard to do the steps for you.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1481199104475905802-4750796282146028847?l=kpipartners.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/4750796282146028847'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/4750796282146028847'/><link rel='alternate' type='text/html' href='http://kpipartners.blogspot.com/2009/06/multi-select-with-all-values-choice-and.html' title='Multi-Select with an All Values Choice, and Fragments'/><author><name>Kurt Wolff</name><uri>http://www.blogger.com/profile/07922051559586431987</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><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/_vx0ImXMybTU/Siy5dgIo9QI/AAAAAAAAAUU/g46EXPa6hKM/s72-c/image_thumb.png?imgmax=800' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-1481199104475905802.post-139449954668334696</id><published>2009-05-25T23:34:00.001-07:00</published><updated>2009-05-25T23:49:20.133-07:00</updated><title type='text'>Evaluate with Date Functions</title><content type='html'>&lt;p&gt;If you’re not concerned about database portability – for example, you use Oracle and that’s that, forever – then the evaluate function in OBIEE can be useful. However, using the evaluate function can be tricky, and the documentation could be better. &lt;/p&gt;  &lt;p&gt;Here’s an example of using Evaluate with the Oracle next_day function, which returns the date of the next specified day of the week following any given date. This function is useful for calculating the week ending date for any given date. If your standard week ends on Saturday, then the expression you would use would be next_day(&amp;lt;date&amp;gt;, ‘saturday’).&amp;#160; This is quite simple, but how do you use it within OBIEE?&lt;/p&gt;  &lt;p&gt;Here’s a simple subject area in OBIEE which I used to test how to use next_day( ). It contains two logical tables: a Days table and a Facts table. The facts table shows how many calls occurred on any day.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/ShuNKry32uI/AAAAAAAAARg/KEsVSknQTu4/s1600-h/image%5B5%5D.png"&gt;&lt;img title="image" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="162" alt="image" src="http://lh3.ggpht.com/_vx0ImXMybTU/ShuNLOa_nqI/AAAAAAAAARk/muyqZaqr6co/image_thumb%5B1%5D.png?imgmax=800" width="155" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;As these query results show, there is one call per day.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/ShuNLXihgPI/AAAAAAAAARo/IGR8pyQAurI/s1600-h/image%5B2%5D.png"&gt;&lt;img title="image" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="244" alt="image" src="http://lh4.ggpht.com/_vx0ImXMybTU/ShuNLy0xycI/AAAAAAAAARs/0yZqdTaRySw/image_thumb.png?imgmax=800" width="217" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;The objective was to sum up the number of calls for each Saturday-ending week using the Oracle next_day function.&lt;/p&gt;  &lt;p&gt;When you use the evaluate function in OBIEE, the expression builder presents you with this template:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/ShuNM9vplQI/AAAAAAAAARw/8Chx4L22HV4/s1600-h/image%5B47%5D.png"&gt;&lt;img title="image" style="display: inline" height="87" alt="image" src="http://lh3.ggpht.com/_vx0ImXMybTU/ShuNNMj363I/AAAAAAAAAR0/WYBkWSVZqIg/image_thumb%5B31%5D.png?imgmax=800" width="299" /&gt;&lt;/a&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;Following the template, you would write this:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/ShuNNWY1VCI/AAAAAAAAAR4/p1-ThN1JrHk/s1600-h/image%5B48%5D.png"&gt;&lt;img title="image" style="display: inline" height="36" alt="image" src="http://lh5.ggpht.com/_vx0ImXMybTU/ShuNOPu94VI/AAAAAAAAAR8/AN-QB0aPdoc/image_thumb%5B32%5D.png?imgmax=800" width="367" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;However, this syntax produced a “Union of non-compatible types” error.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/ShuNOoSrhVI/AAAAAAAAASA/bpTVdZGn05A/s1600-h/image%5B114%5D.png"&gt;&lt;img title="image" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="147" alt="image" src="http://lh3.ggpht.com/_vx0ImXMybTU/ShuNO3oYdoI/AAAAAAAAASE/ySFJTrL3_Ak/image_thumb%5B74%5D.png?imgmax=800" width="346" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;I do not know what this error is really saying. Does the column name have to be enclosed in single quotes to make it a string?&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/ShuNPZEETYI/AAAAAAAAATw/RKtUe8msjQg/s1600-h/image%5B115%5D.png"&gt;&lt;img title="image" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="37" alt="image" src="http://lh6.ggpht.com/_vx0ImXMybTU/ShuNPjocP0I/AAAAAAAAAT0/adFjx6EyS6o/image_thumb%5B75%5D.png?imgmax=800" width="359" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;The administration tool accepts this, but you get a SQL error when you use it in a query.&lt;/p&gt;  &lt;p&gt;ORA-01858: a non-numeric character was found where a numeric was expected&lt;/p&gt;  &lt;p&gt;The reason is obvious when you look at the SQL generated:&lt;/p&gt;  &lt;p&gt;select &lt;b&gt;next_day&lt;/b&gt;&lt;b&gt;&lt;/b&gt;('&amp;quot;XE&amp;quot;.&amp;quot;&amp;quot;.&amp;quot;XE&amp;quot;.&amp;quot;DAYS&amp;quot;.&amp;quot;DATE1&amp;quot;','saturday') as C1    &lt;br /&gt;,&lt;b&gt;sum&lt;/b&gt;&lt;b&gt;&lt;/b&gt;(T3392.CALLS) as C2    &lt;br /&gt;from DAYS T3386    &lt;br /&gt;,DAYSCALLS T3392    &lt;br /&gt;where (T3386.id = T3392.id)    &lt;br /&gt;group by &lt;b&gt;next_day&lt;/b&gt;&lt;b&gt;&lt;/b&gt;('&amp;quot;XE&amp;quot;.&amp;quot;&amp;quot;.&amp;quot;XE&amp;quot;.&amp;quot;DAYS&amp;quot;.&amp;quot;DATE1&amp;quot;','saturday')    &lt;br /&gt;order by C1&lt;/p&gt;  &lt;p&gt;Instead of providing a column identifier that had a date data type as an argument in the next_day function, the SQL contained a string value.&lt;/p&gt;  &lt;p&gt;My next thought when I encountered this was to include the column name as part of the first “str_Expr”.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/ShuNPwRoYaI/AAAAAAAAAUA/7_bBkB11pZ4/s1600-h/image%5B121%5D.png"&gt;&lt;img title="image" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="25" alt="image" src="http://lh4.ggpht.com/_vx0ImXMybTU/ShuNQI3UIMI/AAAAAAAAAUE/ymrELp9E4yA/image_thumb%5B81%5D.png?imgmax=800" width="380" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;However, this produced another Oracle error:&lt;/p&gt;  &lt;p&gt;ORA-01741: illegal zero-length identifier&lt;/p&gt;  &lt;p&gt;So to fix this I edited the column identifier. &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/ShuNQa0Xo3I/AAAAAAAAASY/5WKio946irI/s1600-h/image%5B61%5D.png"&gt;&lt;img title="image" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="25" alt="image" src="http://lh5.ggpht.com/_vx0ImXMybTU/ShuNQ5JuKBI/AAAAAAAAASc/rTWXV8B8r6U/image_thumb%5B43%5D.png?imgmax=800" width="352" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;The administration tool accepted this, but it produced a different SQL error at run time:&lt;/p&gt;  &lt;p&gt;ORA-00904: &amp;quot;DAYS&amp;quot;.&amp;quot;DATE1&amp;quot;: invalid identifier&lt;/p&gt;  &lt;p&gt;The reason this is an invalid identifier is that the SQL OBIEE generated included a table alias for the DAYS table, T3392.&lt;/p&gt;  &lt;p&gt;select &lt;b&gt;next_day&lt;/b&gt;&lt;b&gt;&lt;/b&gt;(&amp;quot;DAYS&amp;quot;.&amp;quot;DATE1&amp;quot;,'saturday') as C1    &lt;br /&gt;,&lt;b&gt;sum&lt;/b&gt;&lt;b&gt;&lt;/b&gt;(T3392.CALLS) as C2    &lt;br /&gt;from DAYS T3386    &lt;br /&gt;,DAYSCALLS T3392    &lt;br /&gt;where (T3386.id = T3392.id)    &lt;br /&gt;group by &lt;b&gt;next_day&lt;/b&gt;&lt;b&gt;&lt;/b&gt;(&amp;quot;DAYS&amp;quot;.&amp;quot;DATE1&amp;quot;,'saturday')    &lt;br /&gt;order by C1&lt;/p&gt;  &lt;p&gt;To fix this, I deleted the DAYS identifier in the function. This might work, but not if you have a DATE1 column defined in more than one table in the FROM clause. This data contained a DATE1 column both in the DAYS table and DAYSCALLS table. Therefore, the query produced another Oracle error.&lt;/p&gt;  &lt;p&gt;ORA-00918: column ambiguously defined&lt;/p&gt;  &lt;p&gt;To fix this, a table identifier was needed. Since OBIEE used T3386, I included this in the evaluate function.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/ShuNRCPsw6I/AAAAAAAAASg/dTrnSunAuUE/s1600-h/image%5B67%5D.png"&gt;&lt;img title="image" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="28" alt="image" src="http://lh4.ggpht.com/_vx0ImXMybTU/ShuNRUf7vsI/AAAAAAAAASk/Kr98LIRHqlI/image_thumb%5B47%5D.png?imgmax=800" width="375" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;The administration tool accepted this, and OBIEE generated correct SQL that Oracle accepted.&lt;/p&gt;  &lt;p&gt;select &lt;b&gt;next_day&lt;/b&gt;&lt;b&gt;&lt;/b&gt;(&amp;quot;T3386&amp;quot;.&amp;quot;DATE1&amp;quot;,'saturday') as C1    &lt;br /&gt;,&lt;b&gt;sum&lt;/b&gt;&lt;b&gt;&lt;/b&gt;(T3392.CALLS) as C2    &lt;br /&gt;from DAYS T3386    &lt;br /&gt;,DAYSCALLS T3392    &lt;br /&gt;where (T3386.id = T3392.id)    &lt;br /&gt;group by &lt;b&gt;next_day&lt;/b&gt;&lt;b&gt;&lt;/b&gt;(&amp;quot;T3386&amp;quot;.&amp;quot;DATE1&amp;quot;,'saturday')    &lt;br /&gt;order by C1&lt;/p&gt;  &lt;p&gt;However, the results in Answers were not correct.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/ShuNRiG6JzI/AAAAAAAAASo/qNJks9qlie8/s1600-h/image%5B71%5D.png"&gt;&lt;img title="image" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="240" alt="image" src="http://lh6.ggpht.com/_vx0ImXMybTU/ShuNSBDKyUI/AAAAAAAAASs/Wunx1A0URFI/image_thumb%5B49%5D.png?imgmax=800" width="120" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Notice that instead of getting a week ending date, we see something that looks like a month, with the sum by week presented as separate rows within the month. Even though the function next_day returns a date, OBIEE treated it as text.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/ShuNSTFzanI/AAAAAAAAASw/60J5KfGRj28/s1600-h/image%5B76%5D.png"&gt;&lt;img title="image" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="89" alt="image" src="http://lh3.ggpht.com/_vx0ImXMybTU/ShuNTV82YiI/AAAAAAAAAS0/Orx6uCIibnI/image_thumb%5B52%5D.png?imgmax=800" width="243" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;To fix this, I added AS DATE in the formula to declare that the returned value was a date.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/ShuNTr0AIlI/AAAAAAAAAS4/0KjhP_nJUyE/s1600-h/image%5B83%5D.png"&gt;&lt;img title="image" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="135" alt="image" src="http://lh3.ggpht.com/_vx0ImXMybTU/ShuNT2bcKSI/AAAAAAAAAS8/-RgoiK9IEKg/image_thumb%5B57%5D.png?imgmax=800" width="420" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt; After I refreshed metadata on the presentation server, the query now produced the desired results.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/ShuNUPHbUxI/AAAAAAAAATA/5zzzc8ECoUw/s1600-h/image%5B86%5D.png"&gt;&lt;img title="image" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="244" alt="image" src="http://lh6.ggpht.com/_vx0ImXMybTU/ShuNUnDcemI/AAAAAAAAATE/vJ12nFopFq4/image_thumb%5B58%5D.png?imgmax=800" width="125" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Looking at column properties, everything seemed correct. The column was being treated as a date.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/ShuNU9u7oYI/AAAAAAAAATI/qugejc5twJk/s1600-h/image%5B94%5D.png"&gt;&lt;img title="image" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="108" alt="image" src="http://lh5.ggpht.com/_vx0ImXMybTU/ShuNVHapdZI/AAAAAAAAATM/eEygSnUelwg/image_thumb%5B62%5D.png?imgmax=800" width="348" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;But look what happened – see the X axis -- when I graphed this.&amp;#160; The X-axis showed question marks instead of week ending dates.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/ShuNVhlgvlI/AAAAAAAAATQ/IxzwhWeX0bs/s1600-h/image%5B90%5D.png"&gt;&lt;img title="image" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="174" alt="image" src="http://lh6.ggpht.com/_vx0ImXMybTU/ShuNVwF7GbI/AAAAAAAAATU/hfgYWprY0u4/image_thumb%5B60%5D.png?imgmax=800" width="344" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;After adding the graph, I examined the column properties.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/ShuNWEBDjKI/AAAAAAAAATY/xxjf3CN9RJ0/s1600-h/image%5B98%5D.png"&gt;&lt;img title="image" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="116" alt="image" src="http://lh3.ggpht.com/_vx0ImXMybTU/ShuNWkBhBcI/AAAAAAAAATc/zbktogRy0Dg/image_thumb%5B64%5D.png?imgmax=800" width="356" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;The column had reverted to text!&lt;/p&gt;  &lt;p&gt;To get OBIEE to recognize the results of the evaluate function as a date, one solution that seemed to work was to use the Oracle to_date function along with the next_day function.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/ShuNXJN_9iI/AAAAAAAAATg/S1DyupRDlJc/s1600-h/image%5B112%5D.png"&gt;&lt;img title="image" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="29" alt="image" src="http://lh4.ggpht.com/_vx0ImXMybTU/ShuNXUHnlgI/AAAAAAAAATk/AWqhhiers_Q/image_thumb%5B72%5D.png?imgmax=800" width="399" border="0" /&gt;&lt;/a&gt;Now the graph showed week ending dates on the x-axis. &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/ShuNXqHP1MI/AAAAAAAAATo/Jg4a1ID-paI/s1600-h/image%5B113%5D.png"&gt;&lt;img title="image" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="228" alt="image" src="http://lh5.ggpht.com/_vx0ImXMybTU/ShuNYCqNQEI/AAAAAAAAATs/OD-6Ghmki2U/image_thumb%5B73%5D.png?imgmax=800" width="445" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1481199104475905802-139449954668334696?l=kpipartners.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/139449954668334696'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/139449954668334696'/><link rel='alternate' type='text/html' href='http://kpipartners.blogspot.com/2009/05/evaluate-with-date-functions.html' title='Evaluate with Date Functions'/><author><name>Kurt Wolff</name><uri>http://www.blogger.com/profile/07922051559586431987</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><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/_vx0ImXMybTU/ShuNLOa_nqI/AAAAAAAAARk/muyqZaqr6co/s72-c/image_thumb%5B1%5D.png?imgmax=800' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-1481199104475905802.post-6105188331099879485</id><published>2009-05-09T14:37:00.001-07:00</published><updated>2009-05-09T15:30:26.820-07:00</updated><title type='text'>N:N Fact:Dimension</title><content type='html'>&lt;p&gt;[Note: the following discussion is based on OBIEE version 10.1.3.4.0 and Oracle XE version 10G, both running on Windows. The behavior of other versions of OBIEE could be different.] &lt;p&gt;Typically dimensions have a 1:N relationship to facts. For example, consider a period dimension table at the day level and a fact table containing sales by day and product. For every row in the dimension table there can be many rows in the fact table. However, for every row in the fact table, there is only one associated row in the period table.  &lt;p&gt;However, sometimes things are more complicated. In some cases, each row in the fact table could be associated with many rows in a dimension table. This posting is about modeling this kind of relationship in OBIEE. &lt;p&gt;Let’s take an example where this could occur. When a customer calls a call center, the customer could discuss many topics. If there is a “topic” dimension (a dimension that lists the topics that could be discussed), there could be be many topics associated with any given call.  &lt;p&gt;For example, consider this Calls table. It contains data for 20 calls. 6&amp;nbsp; occurred in May, 6 in June, 5 in July, and 3 in August.&amp;nbsp; Each call was one minute long.  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/SgX3ZUzKwhI/AAAAAAAAALo/igBgxG7d8Xs/s1600-h/image%5B20%5D.png"&gt;&lt;img title="clip_image002" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="244" alt="clip_image002" src="http://lh6.ggpht.com/_vx0ImXMybTU/SgYD5kURsxI/AAAAAAAAAQE/cer2l_iPxFc/clip_image002%5B3%5D.gif?imgmax=800" width="125" border="0"&gt;&lt;/a&gt; &lt;p&gt;There are five topics in the Topics table, four specific topics and a catch-all “Other”.  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/SgX3ZyvsrvI/AAAAAAAAALw/bFApaNRVCSg/s1600-h/image6.png"&gt;&lt;img title="clip_image004" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="188" alt="clip_image004" src="http://lh6.ggpht.com/_vx0ImXMybTU/SgYD6H2jmAI/AAAAAAAAAQI/AQCKPP1Hroc/clip_image004%5B3%5D.gif?imgmax=800" width="151" border="0"&gt;&lt;/a&gt; &lt;p&gt;One way to store which topics were discussed in which call is with the use of a table that associates topics with calls. Such a table can go by several names: “relationship table”, “associative table”,&amp;nbsp; “helper table”, “owner-member table”, or “bridge table” are some terms that have been used. Some of the rows in this table (let’s call it “CallsTopics”) could look like this: &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/SgX3aj14gtI/AAAAAAAAAL4/HIyeSghU4rc/s1600-h/image9.png"&gt;&lt;img title="clip_image006" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="191" alt="clip_image006" src="http://lh5.ggpht.com/_vx0ImXMybTU/SgYD6fRF8fI/AAAAAAAAAQM/0uqJUMr98WE/clip_image006%5B3%5D.gif?imgmax=800" width="174" border="0"&gt;&lt;/a&gt; &lt;p&gt;This table tells us that call 1 was entirely about Bills. Call 2 was about the Bills and Other. Call 3 was about Instructions and Other. In our example database, this table has 36 rows. &lt;p&gt;The physical database schema in this data model looks like this: &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/SgX3bIcA35I/AAAAAAAAAMA/mOjj7MfGb3w/s1600-h/image13.png"&gt;&lt;img title="clip_image008" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="91" alt="clip_image008" src="http://lh5.ggpht.com/_vx0ImXMybTU/SgYD6jJkcsI/AAAAAAAAAQQ/k_uW4W8x1ZM/clip_image008%5B3%5D.gif?imgmax=800" width="244" border="0"&gt;&lt;/a&gt; &lt;p&gt;This model could be more complex. In real life there could (and should) be a period dimension table associated with the calls. There could also be a customer dimensions, a call center employee dimension, and perhaps other dimensions. In our simple example, however, we will work with just these three tables. &lt;p&gt;The main facts are the count of calls and the duration of calls. The aggregation rule for “# Calls” is count distinct. The aggregation rule for Duration is sum.&amp;nbsp; Both of these facts come from the Calls table. In most schemas, the fact table is at the N end of all the join relationships that touch it. Notice that this schema is different: the CallTopics table is the one that is at the N end of the joins. &lt;p&gt;When modeling this in OBIEE, consider that there are two pathways to the &lt;strong&gt;Calls&lt;/strong&gt; table. One logical path is from the &lt;strong&gt;Calls&lt;/strong&gt; table itself used as a dimension table. The other is from the &lt;strong&gt;Topics&lt;/strong&gt; table via the &lt;strong&gt;CallTopics&lt;/strong&gt; table.  &lt;p&gt;One way to model these two pathways is to create two logical fact table sources. One would contain the &lt;strong&gt;Calls&lt;/strong&gt; table alone. The other would contain &lt;strong&gt;Calls&lt;/strong&gt; and &lt;strong&gt;CallTopics&lt;/strong&gt;, as shown in the following screen shot. &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/SgX3bntyo2I/AAAAAAAAAMI/LRkaBYp4jE8/s1600-h/image%5B162%5D.png"&gt;&lt;img title="clip_image010" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="63" alt="clip_image010" src="http://lh4.ggpht.com/_vx0ImXMybTU/SgYD7FhJFbI/AAAAAAAAAQU/sFooH9GXMaI/clip_image010%5B3%5D.gif?imgmax=800" width="244" border="0"&gt;&lt;/a&gt; &lt;p&gt;The entire business model looks like this. &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/SgX3ciEot-I/AAAAAAAAAMQ/tEUnIbW6CeM/s1600-h/image%5B171%5D.png"&gt;&lt;img title="clip_image012" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="201" alt="clip_image012" src="http://lh5.ggpht.com/_vx0ImXMybTU/SgYD7YjxV4I/AAAAAAAAAQY/tgyj58FIbvc/clip_image012%5B3%5D.gif?imgmax=800" width="173" border="0"&gt;&lt;/a&gt; &lt;p&gt;By the way, the business model diagram shows the normal 1:N relationships between logical dimension tables and logical fact tables in that the logical fact table is at the N end of every logical join that touches it.&lt;pre&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/SgX3dPURNWI/AAAAAAAAAMY/Oul9w9c9C_A/s1600-h/image%5B198%5D.png"&gt;&lt;img title="clip_image014" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="180" alt="clip_image014" src="http://lh6.ggpht.com/_vx0ImXMybTU/SgYD7w3MZbI/AAAAAAAAAQc/p_mZu75XNFk/clip_image014%5B3%5D.gif?imgmax=800" width="244" border="0"&gt;&lt;/a&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;In the business model, the two fact table sources can be thought of as being at two different levels of aggregation. With two dimensions, Calls and Topics, the Calls source is at the Total level for Topics.&amp;nbsp; &lt;/p&gt;&lt;pre&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/SgX3dnXtG7I/AAAAAAAAAOQ/DhSotsTsw18/s1600-h/image%5B217%5D.png"&gt;&lt;img title="clip_image016" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="65" alt="clip_image016" src="http://lh3.ggpht.com/_vx0ImXMybTU/SgYD8K8sOmI/AAAAAAAAAQg/6NGGTNVCt3w/clip_image016%5B3%5D.gif?imgmax=800" width="244" border="0"&gt;&lt;/a&gt; &lt;/pre&gt;&lt;br /&gt;&lt;p&gt;The other source, “CallsAndTopics”, is at the detail level for the two dimensions.&lt;/p&gt;&lt;pre&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/SgX3esS4RNI/AAAAAAAAAOg/LIgPo6n2kPI/s1600-h/image%5B216%5D.png"&gt;&lt;img title="clip_image018" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="62" alt="clip_image018" src="http://lh6.ggpht.com/_vx0ImXMybTU/SgYD8XZu_FI/AAAAAAAAAQk/HWddF1h8Gbg/clip_image018%5B3%5D.gif?imgmax=800" width="244" border="0"&gt;&lt;/a&gt;&amp;nbsp; &lt;/pre&gt;&lt;br /&gt;&lt;p&gt;A query for facts by topic using this business model produced these results:&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/SgX3fFFB8yI/AAAAAAAAAMw/cvYVhS4supo/s1600-h/image%5B165%5D.png"&gt;&lt;img title="clip_image020" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="148" alt="clip_image020" src="http://lh6.ggpht.com/_vx0ImXMybTU/SgYD8tuZ1eI/AAAAAAAAAQo/R76LSUrxN3c/clip_image020%5B3%5D.gif?imgmax=800" width="198" border="0"&gt;&lt;/a&gt;&lt;br /&gt;&lt;p&gt;The way to interpret the numbers in the Tot Duration column is that for each topic, this is the sum of the duration of calls that included that topic. In other words, 11 phone calls with a total duration of 11 minutes included the topic of billing. However, the Grand Total of 36 is a more questionable number. Shouldn’t the “Tot Duration” Grand Total be 20?&lt;br /&gt;&lt;p&gt;It turns out that the value shown for the Duration Grand Total depends on the method of aggregation that is being used. The aggregation rule is set in the Edit Column Formula dialog. To begin with, the default aggregation rule is inherited from the aggregation rule set for the column in the metadata. In this case, it is SUM.&lt;br /&gt;&lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/SgX3fkKfVII/AAAAAAAAAM4/UBR8l5mqHpU/s1600-h/image%5B175%5D.png"&gt;&lt;img title="clip_image022" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="244" alt="clip_image022" src="http://lh4.ggpht.com/_vx0ImXMybTU/SgYD8-lGlzI/AAAAAAAAAQs/RtCMeaHrLDA/clip_image022%5B3%5D.gif?imgmax=800" width="166" border="0"&gt;&lt;/a&gt;&lt;br /&gt;&lt;p&gt;Notice what happens when the Server Complex Aggregate rule is used instead.&lt;br /&gt;&lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/SgX3gX5iyvI/AAAAAAAAANA/cqQeOZz5VG4/s1600-h/image%5B176%5D.png"&gt;&lt;img title="clip_image024" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="102" alt="clip_image024" src="http://lh5.ggpht.com/_vx0ImXMybTU/SgYD9dWpCrI/AAAAAAAAAQw/FsB-xGmjE4s/clip_image024%5B3%5D.gif?imgmax=800" width="244" border="0"&gt;&lt;/a&gt;&lt;br /&gt;&lt;p&gt;The use of different aggregation rules resulted in different logical SQL, with REPORT_SUM used when the aggregation rule was “Default” (inherited from SUM) and AGGREGATE when “Server Complex Aggregate”.&lt;/p&gt;&lt;pre&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/SgX3hPKUkkI/AAAAAAAAAOs/kBwEpHzblYI/s1600-h/image%5B215%5D.png"&gt;&lt;img title="clip_image026" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="90" alt="clip_image026" src="http://lh6.ggpht.com/_vx0ImXMybTU/SgYD98x4vRI/AAAAAAAAAQ0/zktA4A6Qg74/clip_image026%5B4%5D.gif?imgmax=800" width="343" border="0"&gt;&lt;/a&gt; &lt;/pre&gt;&lt;br /&gt;&lt;p&gt;The different logical SQL statements resulted in different physical SQL statements.&amp;nbsp; &lt;/p&gt;&lt;pre&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;With REPORT_SUM there were two Select statements to the database (sum call duration and count distinct calls by topic in the first select; count distinct calls overall), with an additional six select statements issued to manipulate the results of the first two queries, including calculating the report sum.&amp;nbsp; &lt;/p&gt;&lt;br /&gt;&lt;p&gt;In the case of the AGGREGATE rule, an additional SQL statement was issued to sum the overall duration. This is the query that eliminated the over-counting. Then two additional select statements were used to manipulate the results of the first three queries. &lt;/p&gt;&lt;br /&gt;&lt;p&gt;Note that by changing the aggregation rule to server complex aggregate, then opening the column properties dialog and saving as the system-wide default, the AGGREGATE function will become the default aggregation rule in the future. (Re-read the warning about version-specific behavior at the beginning of this post.)&lt;/p&gt;&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/SgX3hs-BvPI/AAAAAAAAANQ/kAAxbDHQYeI/s1600-h/image%5B195%5D.png"&gt;&lt;img title="clip_image028" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="80" alt="clip_image028" src="http://lh3.ggpht.com/_vx0ImXMybTU/SgYD-di7UjI/AAAAAAAAAQ4/brxeS87zWO8/clip_image028%5B4%5D.gif?imgmax=800" width="322" border="0"&gt;&lt;/a&gt; &lt;/pre&gt;&lt;pre&gt;&amp;nbsp;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/SgX3iSw21TI/AAAAAAAAANY/f9DuHBviUMg/s1600-h/image%5B194%5D.png"&gt;&lt;img title="clip_image030" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="62" alt="clip_image030" src="http://lh3.ggpht.com/_vx0ImXMybTU/SgYD-q6eSGI/AAAAAAAAAQ8/BPV_FDjT5rY/clip_image030%5B4%5D.gif?imgmax=800" width="323" border="0"&gt;&lt;/a&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Another way to model this is to take the CALLTOPICS table out of the fact table source and create a new logical table that acts as a “bridge table” between the Topics table and the Calls table. Note that the Bridge table property is checked here. &lt;/p&gt;&lt;pre&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/SgX3i3ZA53I/AAAAAAAAAO4/Iz6KIYJFT8w/s1600-h/image61%5B1%5D.png"&gt;&lt;img title="clip_image032" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="200" alt="clip_image032" src="http://lh6.ggpht.com/_vx0ImXMybTU/SgYD-10M14I/AAAAAAAAARA/ScwZXN8JHOU/clip_image032%5B3%5D.gif?imgmax=800" width="244" border="0"&gt;&lt;/a&gt; &lt;/pre&gt;&lt;br /&gt;&lt;p&gt;The business model now looks like this. &lt;/p&gt;&lt;pre&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/SgX3jggDXSI/AAAAAAAAAPM/yz5GtmfM-NQ/s1600-h/image56%5B1%5D.png"&gt;&lt;img title="clip_image034" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="84" alt="clip_image034" src="http://lh5.ggpht.com/_vx0ImXMybTU/SgYD_NamEiI/AAAAAAAAARE/Hoc39hh4EoA/clip_image034%5B3%5D.gif?imgmax=800" width="244" border="0"&gt;&lt;/a&gt; &lt;/pre&gt;&lt;br /&gt;&lt;p&gt; The default results are shown here.&lt;/p&gt;&lt;pre&gt;&amp;nbsp;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/SgX3kUl1QZI/AAAAAAAAARI/MS0LN15MgB8/s1600-h/image%5B223%5D.png"&gt;&lt;img title="clip_image036" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="149" alt="clip_image036" src="http://lh5.ggpht.com/_vx0ImXMybTU/SgYD_qfwjeI/AAAAAAAAARQ/GkitAQkkiIs/clip_image036%5B3%5D.gif?imgmax=800" width="200" border="0"&gt;&lt;/a&gt; &lt;/pre&gt;&lt;br /&gt;&lt;p&gt; The logical SQL issued uses the AGGREGATE rule by default. &lt;/p&gt;&lt;pre&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/SgX3k3hRfZI/AAAAAAAAAPk/REKGGIyTUg4/s1600-h/image%5B218%5D.png"&gt;&lt;img title="clip_image038" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="58" alt="clip_image038" src="http://lh3.ggpht.com/_vx0ImXMybTU/SgYD_8BlMoI/AAAAAAAAARU/YMY1RnqrKSk/clip_image038%5B4%5D.gif?imgmax=800" width="311" border="0"&gt;&lt;/a&gt; &lt;/pre&gt;&lt;br /&gt;&lt;p&gt; Switching the aggregation rule in the column formula dialog to SUM changes the results and the logical SQL.&lt;/p&gt;&lt;pre&gt;&amp;nbsp;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/SgX3lrtfFTI/AAAAAAAAAPw/F2DnrN4rXfg/s1600-h/image%5B219%5D.png"&gt;&lt;img title="clip_image040" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="57" alt="clip_image040" src="http://lh4.ggpht.com/_vx0ImXMybTU/SgYEACV1XiI/AAAAAAAAARY/OsaRsTiZwSQ/clip_image040%5B4%5D.gif?imgmax=800" width="308" border="0"&gt;&lt;/a&gt; &lt;/pre&gt;&lt;pre&gt;&amp;nbsp;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/SgX3mFD2A2I/AAAAAAAAAOI/JPhS14IuLiI/s1600-h/image%5B210%5D.png"&gt;&lt;img title="clip_image042" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="148" alt="clip_image042" src="http://lh3.ggpht.com/_vx0ImXMybTU/SgYEASKTFPI/AAAAAAAAARc/PZQBfeHHNlQ/clip_image042%5B3%5D.gif?imgmax=800" width="199" border="0"&gt;&lt;/a&gt; &lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Summary: In data models such as this where you have the possibility of double counting results in totals, be aware that the totals will depend on the aggregation rules set for the measures on the Criteria tab.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Default behavior depends on how the business model is configured and which aggregation rule is being used as the default. &lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1481199104475905802-6105188331099879485?l=kpipartners.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/6105188331099879485'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/6105188331099879485'/><link rel='alternate' type='text/html' href='http://kpipartners.blogspot.com/2009/05/nn-factdimension.html' title='N:N Fact:Dimension'/><author><name>Kurt Wolff</name><uri>http://www.blogger.com/profile/07922051559586431987</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><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/_vx0ImXMybTU/SgYD5kURsxI/AAAAAAAAAQE/cer2l_iPxFc/s72-c/clip_image002%5B3%5D.gif?imgmax=800' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-1481199104475905802.post-5393907214025012212</id><published>2009-04-17T10:39:00.001-07:00</published><updated>2009-04-17T10:39:17.359-07:00</updated><title type='text'>Linear Regression</title><content type='html'>&lt;p&gt;Linear regression is a statistical technique for drawing a line through a set of data points that “best fits” the data. It is a useful and standard technique for quantifying trends.&lt;/p&gt;  &lt;p&gt;For example, consider the following sample data set, graphed as a simulated dot plot in OBI EE.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/Sei-cHAkmCI/AAAAAAAAAIQ/NdlN4WKpfKY/s1600-h/ScreenShot069%5B3%5D.jpg"&gt;&lt;img title="ScreenShot069" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="325" alt="ScreenShot069" src="http://lh5.ggpht.com/_vx0ImXMybTU/Sei-cjhO2UI/AAAAAAAAAIU/mq9Nz0-33wc/ScreenShot069_thumb%5B1%5D.jpg?imgmax=800" width="398" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Fitting a regression line is a two-step process: finding the slope of the line and finding the y-intercept (where the regression line would cross the y axis).&lt;/p&gt;  &lt;p&gt;If you don’t remember the formula for determining the slope from the statistics class you took 10 to 40 years ago (!), you can easily find it with a search on the web. &lt;a href="http://people.hofstra.edu/Stefan_Waner/RealWorld/calctopic1/regression.html"&gt;This site&lt;/a&gt; has this:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/Sei-dIOWm2I/AAAAAAAAAIY/z9m1r8E2mGs/s1600-h/ScreenShot070%5B3%5D.jpg"&gt;&lt;img title="ScreenShot070" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="281" alt="ScreenShot070" src="http://lh6.ggpht.com/_vx0ImXMybTU/Sei-dkW_0EI/AAAAAAAAAIc/GGNa1CMt6dc/ScreenShot070_thumb%5B1%5D.jpg?imgmax=800" width="426" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;You can calculate this formula directly in Answers. I recommend doing it term by term, using a different calculated column for each term in the formula and then combining terms at the end.&lt;/p&gt;  &lt;p&gt;Click on any column in the left panel to get a column to write the first term of the formula in. Here, the column clicked was Revenue.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/Sei-d4DKwNI/AAAAAAAAAIg/qYUGroCdCdc/s1600-h/ScreenShot071%5B2%5D.jpg"&gt;&lt;img title="ScreenShot071" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="77" alt="ScreenShot071" src="http://lh3.ggpht.com/_vx0ImXMybTU/Sei-eUeJ4PI/AAAAAAAAAIk/r2_WtbZVjA8/ScreenShot071_thumb.jpg?imgmax=800" width="244" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;The first term will be x*y. I label the column headings to correspond to the formula I am building.&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/Sei-eleWbTI/AAAAAAAAAIo/P_bHFIbw2X8/s1600-h/ScreenShot074%5B2%5D.jpg"&gt;&lt;img title="ScreenShot074" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="132" alt="ScreenShot074" src="http://lh3.ggpht.com/_vx0ImXMybTU/Sei-e2FjLhI/AAAAAAAAAIs/jcrwh2vhPhU/ScreenShot074_thumb.jpg?imgmax=800" width="244" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/Sei-fdmXuLI/AAAAAAAAAIw/zFhfxOAJ2pQ/s1600-h/ScreenShot075%5B2%5D.jpg"&gt;&lt;img title="ScreenShot075" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="244" alt="ScreenShot075" src="http://lh6.ggpht.com/_vx0ImXMybTU/Sei-fgIDetI/AAAAAAAAAI0/_4DNALGxYcM/ScreenShot075_thumb.jpg?imgmax=800" width="84" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;Then add another column to continue building the formula, using the Column button in the formula window to pick the x*y column already built. This technique saves a lot of headaches down the road as the formula becomes more complex and you start hunting down unmatched parentheses.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/Sei-f1AglXI/AAAAAAAAAI4/05b64-4JSmk/s1600-h/ScreenShot080%5B3%5D.jpg"&gt;&lt;img title="ScreenShot080" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="233" alt="ScreenShot080" src="http://lh3.ggpht.com/_vx0ImXMybTU/Sei-gToRW7I/AAAAAAAAAI8/UhtQS9_NBOA/ScreenShot080_thumb%5B1%5D.jpg?imgmax=800" width="391" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Clicking on x*y gives this formula.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/Sei-gmCBBTI/AAAAAAAAAJA/PfPHby47A7Y/s1600-h/ScreenShot076%5B3%5D.jpg"&gt;&lt;img title="ScreenShot076" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="185" alt="ScreenShot076" src="http://lh4.ggpht.com/_vx0ImXMybTU/Sei-hOBDWdI/AAAAAAAAAJE/dDJybY5HMWw/ScreenShot076_thumb%5B1%5D.jpg?imgmax=800" width="344" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;It is useful to look at results for each step along the way. In this case, the results are not what are needed.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/Sei-hjDyM0I/AAAAAAAAAJI/B854t4dBqxI/s1600-h/ScreenShot077%5B3%5D.jpg"&gt;&lt;img title="ScreenShot077" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="352" alt="ScreenShot077" src="http://lh4.ggpht.com/_vx0ImXMybTU/Sei-iC3X5eI/AAAAAAAAAJM/IsCPo2s520Y/ScreenShot077_thumb%5B1%5D.jpg?imgmax=800" width="175" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;So the formula needs to be modified. I added the “by” clause to get the sum for all rows.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/Sei-iZ94wbI/AAAAAAAAAJQ/lIOe5F15vmM/s1600-h/ScreenShot078%5B3%5D.jpg"&gt;&lt;img title="ScreenShot078" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="184" alt="ScreenShot078" src="http://lh6.ggpht.com/_vx0ImXMybTU/Sei-iuRLUcI/AAAAAAAAAJU/YYsc_oZAQgM/ScreenShot078_thumb%5B1%5D.jpg?imgmax=800" width="362" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/Sei-jLuxMKI/AAAAAAAAAJY/mOu7qjD1x2E/s1600-h/ScreenShot079%5B3%5D.jpg"&gt;&lt;img title="ScreenShot079" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="399" alt="ScreenShot079" src="http://lh4.ggpht.com/_vx0ImXMybTU/Sei-jlXRpoI/AAAAAAAAAJc/zCtQgAQlC7w/ScreenShot079_thumb%5B1%5D.jpg?imgmax=800" width="198" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;As you continue building the regression formula step by step, one of the most problematic seems to be the calculation of the number of rows (N in the formula).&lt;/p&gt;  &lt;p&gt;The formula shown here does not work and gives you an error message that is, parenthetically, the answer to the question “What use are logical keys in fact tables?”&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/Sei-jy7ByBI/AAAAAAAAAJg/u8p12zNtDUc/s1600-h/ScreenShot082%5B3%5D.jpg"&gt;&lt;img title="ScreenShot082" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="176" alt="ScreenShot082" src="http://lh4.ggpht.com/_vx0ImXMybTU/Sei-kRguXNI/AAAAAAAAAJk/RM6AK3fw_ns/ScreenShot082_thumb%5B1%5D.jpg?imgmax=800" width="385" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;State: HY000. Code: 10058. [NQODBC] [SQL_STATE: HY000] [nQSError: 10058] A general error has occurred. [nQSError: 15036] Logical table Facts needs a primary key to support count aggregates on that table. (HY000)&lt;/p&gt;  &lt;p&gt;However, instead of creating fact table keys, calculate the count another way. The following formula appears to work (but oddly, as we’ll see, only for a while).&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/Sei-kk_zlhI/AAAAAAAAAJo/5uTS0JGViOQ/s1600-h/ScreenShot081%5B3%5D.jpg"&gt;&lt;img title="ScreenShot081" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="215" alt="ScreenShot081" src="http://lh3.ggpht.com/_vx0ImXMybTU/Sei-k0jbHmI/AAAAAAAAAJs/s6PZ0MF1x0U/ScreenShot081_thumb%5B1%5D.jpg?imgmax=800" width="368" border="0" /&gt;&lt;/a&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/Sei-leINEoI/AAAAAAAAAJw/0B0OSSK0tek/s1600-h/ScreenShot083%5B3%5D.jpg"&gt;&lt;img title="ScreenShot083" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="246" alt="ScreenShot083" src="http://lh3.ggpht.com/_vx0ImXMybTU/Sei-lmE4eUI/AAAAAAAAAJ0/hXrLYhRi9lo/ScreenShot083_thumb%5B1%5D.jpg?imgmax=800" width="320" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Continue building.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/Sei-mPnNOfI/AAAAAAAAAJ4/hqsxJ6mMs7c/s1600-h/ScreenShot084%5B3%5D.jpg"&gt;&lt;img title="ScreenShot084" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="78" alt="ScreenShot084" src="http://lh6.ggpht.com/_vx0ImXMybTU/Sei-mXS0dNI/AAAAAAAAAJ8/zWAV58Vjn7Q/ScreenShot084_thumb%5B1%5D.jpg?imgmax=800" width="358" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/Sei-mpl6taI/AAAAAAAAAKA/_UKKcASx1xI/s1600-h/ScreenShot085%5B3%5D.jpg"&gt;&lt;img title="ScreenShot085" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="148" alt="ScreenShot085" src="http://lh3.ggpht.com/_vx0ImXMybTU/Sei-nFzu_aI/AAAAAAAAAKE/BxMYmI3cZok/ScreenShot085_thumb%5B1%5D.jpg?imgmax=800" width="353" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Tip: Use the Power() function in your formulas when you can.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/Sei-nZazQFI/AAAAAAAAAKI/Ck2PxGX1B58/s1600-h/ScreenShot086%5B3%5D.jpg"&gt;&lt;img title="ScreenShot086" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="121" alt="ScreenShot086" src="http://lh3.ggpht.com/_vx0ImXMybTU/Sei-nlnApOI/AAAAAAAAAKM/SnLzBcpDN8M/ScreenShot086_thumb%5B1%5D.jpg?imgmax=800" width="355" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Now, the odd thing mentioned above happens. All these columns compute.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/Sei-oFLmJiI/AAAAAAAAAKQ/AVe7embOmPE/s1600-h/ScreenShot089%5B3%5D.jpg"&gt;&lt;img title="ScreenShot089" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="59" alt="ScreenShot089" src="http://lh3.ggpht.com/_vx0ImXMybTU/Sei-owtrmKI/AAAAAAAAAKU/W34L8KnXea4/ScreenShot089_thumb%5B1%5D.jpg?imgmax=800" width="380" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;However, when you subtract the last column from the next to last to get the denominator in the formula, Answers throws a puzzling error.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/Sei-pAF0xoI/AAAAAAAAAKY/5C03JNar-S8/s1600-h/ScreenShot090%5B6%5D.jpg"&gt;&lt;img title="ScreenShot090" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="88" alt="ScreenShot090" src="http://lh6.ggpht.com/_vx0ImXMybTU/Sei-pRqESBI/AAAAAAAAAKc/7FcBkAoZGXY/ScreenShot090_thumb%5B4%5D.jpg?imgmax=800" width="427" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;State: HY000. Code: 10058. [NQODBC] [SQL_STATE: HY000] [nQSError: 10058] A general error has occurred. [nQSError: 22023] An arithmetic operation is being carried out on a non-numeric type. (HY000)&lt;/p&gt;  &lt;p&gt;At least one of the two numbers in the final column is now regarded as non-numeric?&lt;/p&gt;  &lt;p&gt;A solution to this seems to be to change the formula for N, which was max(count(rsum(1))). Both max(rsum(1)) or sum(1 by) work.&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/Sei-qCzdSAI/AAAAAAAAAKg/54bvQFfWN7I/s1600-h/ScreenShot091%5B3%5D.jpg"&gt;&lt;img title="ScreenShot091" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="81" alt="ScreenShot091" src="http://lh6.ggpht.com/_vx0ImXMybTU/Sei-qV3sUvI/AAAAAAAAAKk/6jrjcTQt_Ao/ScreenShot091_thumb%5B1%5D.jpg?imgmax=800" width="396" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;When you have the numerator(the first column in the following screen shot) and denominator (next to last column) built, calculate the slope.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/Sei-q6_KbEI/AAAAAAAAAKo/0JyrdEGQZd8/s1600-h/ScreenShot092%5B3%5D.jpg"&gt;&lt;img title="ScreenShot092" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="158" alt="ScreenShot092" src="http://lh5.ggpht.com/_vx0ImXMybTU/Sei-rNc0C7I/AAAAAAAAAKs/5_KygbPPwJQ/ScreenShot092_thumb%5B1%5D.jpg?imgmax=800" width="389" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;To plot the regression line, you also need the y intercept. The Criteria tab already has all the terms you need to plug into the intercept formula.&lt;/p&gt;  &lt;p&gt;Once you have the slope and intercept, the regression line (values of y vs x) are easily calculated with the formula y=mx+b explained above (m is the slope, b is the y-intercept).&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/Sei-rf6Q3uI/AAAAAAAAAKw/jIFdEhc-yS0/s1600-h/ScreenShot093%5B2%5D.jpg"&gt;&lt;img title="ScreenShot093" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="119" alt="ScreenShot093" src="http://lh5.ggpht.com/_vx0ImXMybTU/Sei-rz9pAAI/AAAAAAAAAK0/1Khc4z46Pyo/ScreenShot093_thumb.jpg?imgmax=800" width="197" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;The plot looks like this:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/Sei-sPlYMeI/AAAAAAAAAK4/qWeR8d-cu4k/s1600-h/image%5B3%5D.png"&gt;&lt;img title="image" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="242" alt="image" src="http://lh4.ggpht.com/_vx0ImXMybTU/Sei-sZQOinI/AAAAAAAAAK8/cqv9ltCqi-M/image_thumb%5B1%5D.png?imgmax=800" width="361" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;This is a line chart. Revenue is shown with blue round&amp;#160; symbols and a 0 pixel line width. The regression line is a red dotted line with symbols turned off.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/Sei-tTbGQLI/AAAAAAAAALA/sRi9kZLgnyU/s1600-h/ScreenShot095%5B3%5D.jpg"&gt;&lt;img title="ScreenShot095" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="103" alt="ScreenShot095" src="http://lh4.ggpht.com/_vx0ImXMybTU/Sei-t46gKtI/AAAAAAAAALE/b3vklb2utHA/ScreenShot095_thumb%5B1%5D.jpg?imgmax=800" width="381" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Another statistic that is important is the R-squared statistic, which measures how well the regression line fits the data. The formula for R (the correlation coefficient) is:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/Sei-uKLqBhI/AAAAAAAAALI/TRP0JKZzuXY/s1600-h/ScreenShot096%5B3%5D.jpg"&gt;&lt;img title="ScreenShot096" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="211" alt="ScreenShot096" src="http://lh4.ggpht.com/_vx0ImXMybTU/Sei-u62EMQI/AAAAAAAAALM/RfYLdnXNJ_4/ScreenShot096_thumb%5B1%5D.jpg?imgmax=800" width="366" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;The Criteria tab already contains the formula for the numerator and several terms of the denominator (circled in this picture).&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/Sei-vDv8q-I/AAAAAAAAALQ/rwEPTF_OEYc/s1600-h/image%5B10%5D.png"&gt;&lt;img title="image" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="107" alt="image" src="http://lh6.ggpht.com/_vx0ImXMybTU/Sei-vtkLA_I/AAAAAAAAALU/Zb95pZFK72U/image_thumb%5B4%5D.png?imgmax=800" width="386" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Build the rest needed for the denominator, then calculate R and finally R-squared.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/Sei-v0oiGEI/AAAAAAAAALY/gOAbxTyClRY/s1600-h/ScreenShot098%5B3%5D.jpg"&gt;&lt;img title="ScreenShot098" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="234" alt="ScreenShot098" src="http://lh5.ggpht.com/_vx0ImXMybTU/Sei-wep3x3I/AAAAAAAAALc/EDPe3-MbaSg/ScreenShot098_thumb%5B1%5D.jpg?imgmax=800" width="421" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;The final value, 0.58, is the goodness of fit (goodness ranging from 0, bad, to 1, perfect). &lt;/p&gt;  &lt;p&gt;At this point all the unneeded columns can be hidden or deleted.&lt;/p&gt;  &lt;p&gt;It is always good to translate this into business (i.e. not statistical terms) by renaming things (“Regression” –&amp;gt; “Trend”) and using the narrative view to spell things out as explicitly as possible. Perhaps you can come up with a good business translation for R-square. &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/Sei-wjtZG9I/AAAAAAAAALg/-tovauTMHP8/s1600-h/image%5B14%5D.png"&gt;&lt;img title="image" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="349" alt="image" src="http://lh6.ggpht.com/_vx0ImXMybTU/Sei-xO5ZMlI/AAAAAAAAALk/qHJ0S9XpRsc/image_thumb%5B6%5D.png?imgmax=800" width="426" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1481199104475905802-5393907214025012212?l=kpipartners.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/5393907214025012212'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/5393907214025012212'/><link rel='alternate' type='text/html' href='http://kpipartners.blogspot.com/2009/04/linear-regression.html' title='Linear Regression'/><author><name>Kurt Wolff</name><uri>http://www.blogger.com/profile/07922051559586431987</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><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/_vx0ImXMybTU/Sei-cjhO2UI/AAAAAAAAAIU/mq9Nz0-33wc/s72-c/ScreenShot069_thumb%5B1%5D.jpg?imgmax=800' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-1481199104475905802.post-1205786654279519577</id><published>2009-04-13T15:33:00.001-07:00</published><updated>2009-04-17T10:45:32.824-07:00</updated><title type='text'>Info On the Web About OBIEE</title><content type='html'>&lt;p&gt;It may be strange to be writing this with the intention of posting to a blog, but I thought someone should step back a minute and issue a few words of warning about information that can be found on the web about Oracle BI EE. &lt;/p&gt;  &lt;p&gt;Some of it is downright wrong and will lead you into trouble. Some of it is incomplete and/or not very clear and you will find yourself working hours to figure out how to get the “advice” to actually work. From my experience, the farther away the advice leads you from the “out of the box” product functionality, the more likely either of these outcomes is to occur.&lt;/p&gt;  &lt;p&gt;One of the least well-understood aspects of an OBIEE production environment is maintaining web catalogs, particularly merging “development” web cat elements with the production web catalog. Yes, there is an OBIEE utility for doing this, the Catalog Manager. This utility is the “Fredo” of the OBIEE tools. It no doubt does work in certain situations (nothing is impossible), but my experience is that more often than not it does not work – and betrays you, to boot.&lt;/p&gt;  &lt;p&gt;The cursory documentation, lack of practical examples, and confusing terminology (what does “archive” do and how does it differ – and it does! – from the “archive” menu choice in the web catalog manager), plus the fact that catalog manager is highly version sensitive (to the third decimal) means using it is either going to work perfectly or it is not going to work at all. There is not much middle ground. &lt;/p&gt;  &lt;p&gt;That being the case, it is tempting to believe advice that you don’t need it at all, that “you can copy individual requests, dashboards or whole folders using filesystem copies” as &lt;a href="http://www.rittmanmead.com/2007/12/12/migrating-obiee-reports-between-web-catalogs/"&gt;one blogger&lt;/a&gt; wrote. In fact, this is the last thing you should do, as you risk corrupting the web catalog permanently. Insidiously, it may not be corruption that will show up immediately – in fact, things may look OK after you do it. However, signs of rot may soon appear: permissions that are wrong and cannot be corrected; catalog manager functionality that no longer works; catalog manager complaints about web cat corruption; the Set Privileges page of the web admin ceases to work. By this time, you may have invested more work in the ailing web catalog, and if there’s a way to recover from this situation, Oracle has not published it. The inner structure of a web catalog remains quite undocumented.&lt;/p&gt;  &lt;p&gt;There’s another flavor of this. You may have wished for some functionality in the product which wasn’t there and suddenly one day you read that it actually is via a clever workaround, and it sounds so tempting to try it.&lt;/p&gt;  &lt;p&gt;So you settle down to follow someone’s blog about, say, &lt;a href="http://oraclebizint.wordpress.com/2007/11/26/oracle-bi-ee-101332-same-page-navigation-drills-and-passing-parameters/"&gt;navigating in place&lt;/a&gt;, which is feature that many wished that Oracle BI EE had. Yes, once you figure it out (reading the blog makes it seem quite complex -- partly because the blog introduces complexity into the method that really isn’t necessary) and try it, you discover several limitations the blogger forgot to mention (or hadn’t discovered himself). The technique involves creating a narrative view that dynamically constructs a Go URL within an iFrame on the dashboard page. Navigation from this analysis then happens within the iFrame – i.e. on the same dashboard page.&amp;#160; However, there are limitations. If the dashboard employs a non-default style, the results in the iFrame will not match the dashboard. If you use the Style parameter in the URL to change the style to the dashboard style, it will match but then navigation stops working (you are presented with the logon screen during navigation). Passing a parameter from a dashboard prompt works – as long as the parameter does not contain a space. If navigation does work, there’s no way (besides the browser back button) to return. And to get this far, you have to battle the blog’s text itself – which, for example, does not give you the complete text of the URL used in its own example. In the end, you may, like me, decide that the technique is not really ready for prime time.&lt;/p&gt;  &lt;p&gt;Another example somewhat in the same vein is advice on constructing dynamic column headers from presentation variables. Again, this is functionality that it would be nice to have. However, when you &lt;a href="http://oraclebizint.wordpress.com/2008/01/25/oracle-bi-ee-101332-dynamic-column-headers-using-presentation-variables-sets-and-conditional-formatting/"&gt;read the blog&lt;/a&gt;, you discover that there are more cautions and limitations than there are side effects listed on your favorite prescription medicine. &lt;/p&gt;  &lt;p&gt;Workarounds and functional compromise are part a consultant’s life. Just be careful about what you read on the web. Even very good blogs can give you impractical, wrong, time-draining advice that has not been adequately tested.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1481199104475905802-1205786654279519577?l=kpipartners.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/1205786654279519577'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/1205786654279519577'/><link rel='alternate' type='text/html' href='http://kpipartners.blogspot.com/2009/04/info-on-web-about-obiee.html' title='Info On the Web About OBIEE'/><author><name>Kurt Wolff</name><uri>http://www.blogger.com/profile/07922051559586431987</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></entry><entry><id>tag:blogger.com,1999:blog-1481199104475905802.post-2073760799007460017</id><published>2009-04-05T16:18:00.001-07:00</published><updated>2009-04-05T16:31:39.428-07:00</updated><title type='text'>Fact-Based Partitioning</title><content type='html'>&lt;p&gt;One of the questions that comes up over and over again is how to show all periods in a query when facts don’t exist for all periods or how to show all products when facts don’t exist for all products.&lt;/p&gt;  &lt;p&gt;To illustrate the problem, I created a database of fictional frosting sales. This is a very simple star schema having a period table, a product table, and a fact table containing weekly sales data for the last eleven weeks of 2003. &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/Sdk73Rr5s4I/AAAAAAAAAFY/jcCFLAqWhXE/s1600-h/clip_image002%5B3%5D.jpg"&gt;&lt;img title="clip_image002" style="border: 0px none ; display: inline;" alt="clip_image002" src="http://lh4.ggpht.com/_vx0ImXMybTU/Sdk732x31BI/AAAAAAAAAFc/TJeYJSRxz50/clip_image002_thumb.jpg?imgmax=800" border="0" width="244" height="169" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/Sdk74Z9CStI/AAAAAAAAAFg/l-G0L5vAXjM/s1600-h/clip_image004%5B3%5D.jpg"&gt;&lt;img title="clip_image004" style="border: 0px none ; display: inline;" alt="clip_image004" src="http://lh6.ggpht.com/_vx0ImXMybTU/Sdk74g4WvmI/AAAAAAAAAFk/QTbMhzf7Zus/clip_image004_thumb.jpg?imgmax=800" border="0" width="109" height="193" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;One of the frosting products, the “Party” frosting, was only introduced during the week of 11/6, however. Its sales data look like this – with nothing for the weeks in October.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/Sdk75Dwf7cI/AAAAAAAAAFo/IJGBejA7cH0/s1600-h/clip_image006%5B3%5D.jpg"&gt;&lt;img title="clip_image006" style="border: 0px none ; display: inline;" alt="clip_image006" src="http://lh5.ggpht.com/_vx0ImXMybTU/Sdk75kSJbII/AAAAAAAAAFs/mSP6jHyTEa4/clip_image006_thumb.jpg?imgmax=800" border="0" width="160" height="147" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;How do you construct the metadata to show zero sales in October for Party frosting? &lt;/p&gt;  &lt;p&gt;This is often thought of as a relational database outer join problem, which brings up another question frequently asked:  how do you specify an outer join in the metadata?&lt;/p&gt;  &lt;p&gt;Quite often, people think of this as a property of the physical join between these tables. However, if you look at the physical join properties in the metadata, you can see that the dialog does not give you any way to specify that the join should be an outer join. Notice that the join type drop down control is not active – it is grayed out.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/Sdk752yKyOI/AAAAAAAAAFw/b-9hqnXv7ck/s1600-h/clip_image008%5B3%5D.jpg"&gt;&lt;img title="clip_image008" style="border: 0px none ; display: inline;" alt="clip_image008" src="http://lh5.ggpht.com/_vx0ImXMybTU/Sdk76RNmdyI/AAAAAAAAAF0/cTZqZTt_tHs/clip_image008_thumb.jpg?imgmax=800" border="0" width="244" height="217" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;When we designed OBIEE, we put control over the join type in the business model diagram because it really is a property of the business model – that is, it’s a property of the relationship between the period dimension and the facts. No matter how many period table sources or fact table sources there might be at different levels of granularity (aggregation), the join relationship should be the same within the business model. &lt;/p&gt;  &lt;p&gt;So to change the join type, use the business model diagram and change the join type between the logical tables. In the screen shot shown here, I have changed the join type to “Right Outer”. (“Right” is correct in this context since the table having the rows to preserve, the Weeks logical table, is on the right in this dialog). &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/Sdk77FSRGZI/AAAAAAAAAF4/WniHWAvrChk/s1600-h/clip_image010%5B3%5D.jpg"&gt;&lt;img title="clip_image010" style="border: 0px none ; display: inline;" alt="clip_image010" src="http://lh4.ggpht.com/_vx0ImXMybTU/Sdk77R7B50I/AAAAAAAAAF8/N-8Hub9roqo/clip_image010_thumb.jpg?imgmax=800" border="0" width="244" height="208" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;If you examine the physical SQL after making the change, you can see the outer join being done, with the “right outer join” now translated into a “left outer join” in the SQL since the Weeks table is now “on the left” in the SQL. &lt;/p&gt;  &lt;p&gt;SELECT T2382.DESCRIPTION AS c1,  &lt;br /&gt;T2388.WEEK AS c2,   &lt;br /&gt;sum(T2373.QS) AS c3   &lt;br /&gt;FROM   &lt;br /&gt;FROSTINGPRODUCTS T2382,   &lt;br /&gt;FROSTINGWEEKS T2388 &lt;b&gt;left&lt;/b&gt;&lt;b&gt; outer join&lt;/b&gt;   &lt;br /&gt;FROSTINGFACTS T2373 On T2373.PERIODKEY = T2388.PERIODKEY   &lt;br /&gt;WHERE   &lt;br /&gt;( T2373.PRODKEY = T2382.PRODKEY AND T2382.DESCRIPTION = 'Party' )   &lt;br /&gt;GROUP BY T2382.DESCRIPTION, T2388.WEEK   &lt;br /&gt;ORDER BY c1, c2&lt;/p&gt;  &lt;p&gt;So does this solve the problem? If you look at the results, you can see that it doesn’t. &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/Sdk775UlVPI/AAAAAAAAAGA/PXYKlz7sFYs/s1600-h/clip_image012%5B3%5D.jpg"&gt;&lt;img title="clip_image012" style="border: 0px none ; display: inline;" alt="clip_image012" src="http://lh4.ggpht.com/_vx0ImXMybTU/Sdk78a9VaNI/AAAAAAAAAGE/es7vf9xPf3I/clip_image012_thumb.jpg?imgmax=800" border="0" width="163" height="147" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Perhaps you might be thinking that you might need to outer join the product table to the fact table, too. But this doesn’t give you the right results, either. Even with full outer joins, the results are still exactly the same.&lt;/p&gt;  &lt;p&gt;This is the physical SQL generated by using full outer joins between the dimension tables and the fact table in the business model.&lt;/p&gt;  &lt;p&gt;SELECT   t2382.DESCRIPTION  AS c1   &lt;br /&gt;,t2388.week        AS c2    &lt;br /&gt;,SUM(t2373.qs)     AS c3    &lt;br /&gt;FROM frostingproducts t2382    &lt;br /&gt;  FULL OUTER JOIN (frostingweeks t2388    &lt;br /&gt;        FULL OUTER JOIN frostingfacts t2373    &lt;br /&gt;         ON t2373.periodkey = t2388.periodkey)    &lt;br /&gt;    ON t2373.prodkey = t2382.prodkey    &lt;br /&gt;WHERE    (t2382.DESCRIPTION = 'Party')    &lt;br /&gt;GROUP BY   &lt;br /&gt;t2382.DESCRIPTION    &lt;br /&gt;,t2388.week    &lt;br /&gt;ORDER BY c1,c2&lt;/p&gt;  &lt;p&gt;It produces these results&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/Sdk78g7Q-PI/AAAAAAAAAGI/hIZmQQEqdMM/s1600-h/clip_image014%5B3%5D.jpg"&gt;&lt;img title="clip_image014" style="border: 0px none ; display: inline;" alt="clip_image014" src="http://lh6.ggpht.com/_vx0ImXMybTU/Sdk79KJAr2I/AAAAAAAAAGM/rd4LTzMIyAE/clip_image014_thumb.jpg?imgmax=800" border="0" width="163" height="210" /&gt;&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;Let’s think about how to solve this problem in SQL. First calculate the sum of units sold by week for the ‘Party’ frosting. The SQL for that could look like this:&lt;/p&gt;  &lt;pre&gt;SELECT  &lt;br /&gt;F.PRODKEY&lt;br /&gt;,W.WEEK&lt;br /&gt;,SUM(F.QS) UnitsSold&lt;br /&gt;FROM    &lt;br /&gt;FROSTINGWEEKS W&lt;br /&gt;INNER JOIN FROSTINGFACTS F&lt;br /&gt;ON F.PERIODKEY = W.PERIODKEY&lt;br /&gt;WHERE    F.PRODKEY IN&lt;br /&gt;(SELECT Prodkey&lt;br /&gt;FROM   FROSTINGPRODUCTS&lt;br /&gt;WHERE  Description = 'Party')&lt;br /&gt;GROUP BY F.PRODKEY, W.WEEK&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;This SQL would produce the familiar results we have already seen.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/Sdk79fqe-YI/AAAAAAAAAGQ/fWBI6vE0ebM/s1600-h/clip_image016%5B3%5D.jpg"&gt;&lt;img title="clip_image016" style="border: 0px none ; display: inline;" alt="clip_image016" src="http://lh5.ggpht.com/_vx0ImXMybTU/Sdk794cAq9I/AAAAAAAAAGU/P2suBBtCoAA/clip_image016_thumb.jpg?imgmax=800" border="0" width="200" height="169" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;For the next step, outer join the Products and Weeks table together without using the fact table.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Select P.Description&lt;br /&gt;, P.Prodkey&lt;br /&gt;, W.Week from&lt;br /&gt;frostingproducts P&lt;br /&gt;,frostingweeks W&lt;br /&gt;where P.Description = 'Party'&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/Sdk7-Ia1CtI/AAAAAAAAAGY/vdjQw3ST8D0/s1600-h/clip_image018%5B3%5D.jpg"&gt;&lt;img title="clip_image018" style="border: 0px none ; display: inline;" alt="clip_image018" src="http://lh3.ggpht.com/_vx0ImXMybTU/Sdk7-j9vVqI/AAAAAAAAAGc/1Lvbv7SBvGU/clip_image018_thumb.jpg?imgmax=800" border="0" width="206" height="221" /&gt;&lt;/a&gt;&lt;/p&gt;Then left join the second result set to the first one. In terms of SQL, it would look like this:&lt;br /&gt;&lt;br /&gt;&lt;p&gt;WITH D1 AS&lt;br /&gt;(select F.PRODKEY &lt;br /&gt;     ,W.WEEK &lt;br /&gt;     ,sum(F.QS) UnitsSold &lt;br /&gt;FROM &lt;br /&gt;          FROSTINGWEEKS W inner join &lt;br /&gt;          FROSTINGFACTS F On F.PERIODKEY = W.PERIODKEY &lt;br /&gt;          WHERE F.PRODKEY IN (SELECT&lt;br /&gt;  Prodkey&lt;br /&gt;FROM&lt;br /&gt;FROSTINGPRODUCTS&lt;br /&gt;WHERE&lt;br /&gt;  Description = 'Party')&lt;br /&gt;GROUP BY F.PRODKEY, W.WEEK ORDER BY 2), &lt;br /&gt;D2 AS&lt;br /&gt;(Select P.Description, P.Prodkey, W.Week FROM&lt;br /&gt;frostingproducts P&lt;br /&gt;,frostingweeks W&lt;br /&gt;WHERE P.Description = 'Party')&lt;br /&gt;SELECT D2.Description, D2.Week, nvl(D1.UnitsSold,0) UnitsSold&lt;br /&gt;FROM  D2 left join D1&lt;br /&gt;on D2.Prodkey = D1.Prodkey&lt;br /&gt;AND D2.Week=D1.Week&lt;br /&gt;ORDER BY 2&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/Sdk7-xUK3ZI/AAAAAAAAAGg/EIJjxKNCikg/s1600-h/clip_image020%5B3%5D.jpg"&gt;&lt;img title="clip_image020" style="border: 0px none ; display: inline;" alt="clip_image020" src="http://lh6.ggpht.com/_vx0ImXMybTU/Sdk7_UY_F6I/AAAAAAAAAGk/HSd2Ef0i_Xc/clip_image020_thumb.jpg?imgmax=800" border="0" width="217" height="222" /&gt;&lt;/a&gt;&lt;/p&gt;An OBIEE business model could be constructed to produce SQL like this. One way to do it would be to join the period and product tables into a single logical dimension table (outer joining the underlying physical tables in the logical table source), then left joining this dimension table to the logical fact table.&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Here’s the physical join diagram, with the join between Products and Weeks being a complex join with the condition 1=1.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/Sdk7_hoZ0KI/AAAAAAAAAGo/OgxXHygJ7b0/s1600-h/clip_image022%5B3%5D.jpg"&gt;&lt;img title="clip_image022" style="border: 0px none ; display: inline;" alt="clip_image022" src="http://lh3.ggpht.com/_vx0ImXMybTU/Sdk8AFoxkQI/AAAAAAAAAGs/4m7s4jcaxJg/clip_image022_thumb.jpg?imgmax=800" border="0" width="244" height="154" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;A single logical table source combines Products and Weeks.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/Sdk8ASh7ybI/AAAAAAAAAGw/TuKluMpvtNQ/s1600-h/clip_image024%5B3%5D.jpg"&gt;&lt;img title="clip_image024" style="border: 0px none ; display: inline;" alt="clip_image024" src="http://lh3.ggpht.com/_vx0ImXMybTU/Sdk8A5qG0EI/AAAAAAAAAG0/qPn5x9Jb2Jo/clip_image024_thumb.jpg?imgmax=800" border="0" width="244" height="182" /&gt;&lt;/a&gt;&lt;/p&gt;The business model would contain just two logical tables and a single logical join. Note that it is a Left Outer join.&lt;br /&gt;&lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/Sdk8BD-HFLI/AAAAAAAAAG4/5dTkviYjeNE/s1600-h/clip_image026%5B3%5D.jpg"&gt;&lt;img title="clip_image026" style="border: 0px none ; display: inline;" alt="clip_image026" src="http://lh4.ggpht.com/_vx0ImXMybTU/Sdk8BtK4avI/AAAAAAAAAG8/_DcvcUnZ8uQ/clip_image026_thumb.jpg?imgmax=800" border="0" width="244" height="158" /&gt;&lt;/a&gt;&lt;/p&gt;With this metadata, the logical SQL&lt;br /&gt;&lt;p&gt;SELECT Products.DESCRIPTION&lt;br /&gt;, Weeks.WEEK&lt;br /&gt;, FrostingFacts.UnitsSold &lt;br /&gt;FROM Frosting2&lt;br /&gt;WHERE Products.DESCRIPTION = 'Party'&lt;br /&gt;&lt;/p&gt;&lt;p&gt;produces this physical SQL&lt;/p&gt;SELECT  &lt;p&gt;T2534.DESCRIPTION AS c1&lt;br /&gt;, T2539.WEEK AS c2&lt;br /&gt;, sum(nvl(T2526.QS , 0)) AS c3&lt;br /&gt;FROM &lt;br /&gt;(FROSTINGPRODUCTS T2534 /* FrostingProducts2 */  &lt;br /&gt;full outer join &lt;br /&gt;FROSTINGWEEKS T2539 /* FrostingWeeks2 */  On 1 = 1)  left outer join &lt;br /&gt;FROSTINGFACTS T2526 /* FrostingFacts2 */  &lt;br /&gt;On T2526.PERIODKEY = T2539.PERIODKEY &lt;br /&gt;AND T2526.PRODKEY = T2534.PRODKEY&lt;br /&gt;WHERE&lt;br /&gt;   ( T2534.DESCRIPTION = 'Party' ) &lt;br /&gt;GROUP BY T2534.DESCRIPTION, T2539.WEEK&lt;br /&gt;ORDER BY c1, c2&lt;/p&gt;giving the desired results.&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/Sdk8B3A1CVI/AAAAAAAAAHA/sGirfzkvsTI/s1600-h/clip_image028%5B3%5D.jpg"&gt;&lt;img title="clip_image028" style="border: 0px none ; display: inline;" alt="clip_image028" src="http://lh4.ggpht.com/_vx0ImXMybTU/Sdk8CQPdngI/AAAAAAAAAHE/yyZRag9bP9A/clip_image028_thumb.jpg?imgmax=800" border="0" width="161" height="191" /&gt;&lt;/a&gt;&lt;/p&gt;(Note on the logical SQL: the columns from the single logical table “FrostingProducts2” have been separated into two presentation layer folders, “Products” and “Weeks”).&lt;br /&gt;&lt;p&gt;However, I don’t recommend this as a solution as you may not always want these results, but now you’ve hard-wired them into the metadata.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;A better approach is to take advantage of the concept of “fact-based partitioning”. &lt;em&gt;Fact-based partitioning&lt;/em&gt; is a term meaning that facts are stored (“partitioned”) in separate physical tables. These tables are configured as separate logical fact table sources in the metadata. When facts from two fact table sources are included in a query, the BI Server generates SQL with two query blocks and full outer joins the result sets together -- just the behavior we want.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;In this case, we need to preserve both the weeks and the products and  need a fact that will be returned for all weeks and products specified by the query filter.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;I use a one row table to hold this fact. If you’re using Oracle as a database, you can use the virtual table “Dual”. If you’re using SQL Server, you can create a Select statement without a FROM clause to do the same thing (or you can create a one-row table in the database).&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-weight: bold;"&gt;Step 1&lt;/span&gt;: In the physical layer create a source for the fact that will always be returned. I’ve used a Select statement to create this table:&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/Sdk8CpPtZ1I/AAAAAAAAAHI/SfALhwcv1ZA/s1600-h/clip_image030%5B4%5D.jpg"&gt;&lt;img title="clip_image030" style="border: 0px none ; display: inline;" alt="clip_image030" src="http://lh5.ggpht.com/_vx0ImXMybTU/Sdk8DAoHQUI/AAAAAAAAAHM/wdXk3mEKduo/clip_image030_thumb%5B1%5D.jpg?imgmax=800" border="0" width="286" height="118" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;span style="font-weight: bold;"&gt;Step 2&lt;/span&gt;: Join this table to the dimension tables with a complex join having the condition 1=1.&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;span style="font-weight: bold;"&gt;Step 3&lt;/span&gt;: Add this fact (renamed here as “PreserveDimensions”) as a new logical fact sourced from a new logical table source.&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/Sdk8DRrGfzI/AAAAAAAAAHQ/8VKhT3m9uWw/s1600-h/clip_image032%5B3%5D.jpg"&gt;&lt;img title="clip_image032" style="border: 0px none ; display: inline;" alt="clip_image032" src="http://lh3.ggpht.com/_vx0ImXMybTU/Sdk8D7CyzzI/AAAAAAAAAHU/rjIgDmCRG8Y/clip_image032_thumb.jpg?imgmax=800" border="0" width="155" height="87" /&gt;&lt;/a&gt;&lt;/p&gt;The fact has an aggregation rule of Max. Therefore it will always equal 1.&lt;br /&gt;&lt;p&gt;&lt;span style="font-weight: bold;"&gt;Step 4&lt;/span&gt;: Add this fact to the presentation layer.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-weight: bold;"&gt;Step 5&lt;/span&gt;: Create a query with a filter PreserveDimensions = 1 and save this filter. Call it “Preserve Dimensions = True”.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/Sdk8EYYL8DI/AAAAAAAAAHY/mXgDWng8Iv4/s1600-h/clip_image034%5B3%5D.jpg"&gt;&lt;img title="clip_image034" style="border: 0px none ; display: inline;" alt="clip_image034" src="http://lh6.ggpht.com/_vx0ImXMybTU/Sdk8FCJg33I/AAAAAAAAAHc/ffwvWcwcsHg/clip_image034_thumb.jpg?imgmax=800" border="0" width="244" height="73" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Now to preserve dimensions the user just needs to add this filter to the query. Here’s the query with just a filter on product description:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/Sdk8Fsemk9I/AAAAAAAAAHg/6stx52elfmY/s1600-h/clip_image036%5B3%5D.jpg"&gt;&lt;img title="clip_image036" style="border: 0px none ; display: inline;" alt="clip_image036" src="http://lh3.ggpht.com/_vx0ImXMybTU/Sdk8GJ29K_I/AAAAAAAAAHk/7oZEAXZnpBA/clip_image036_thumb.jpg?imgmax=800" border="0" width="224" height="206" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Now here’s the same query with the Preserve Dimensions filter added:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/Sdk8GVfAbHI/AAAAAAAAAHo/oPrqNkvNpCU/s1600-h/clip_image038%5B3%5D.jpg"&gt;&lt;img title="clip_image038" style="border: 0px none ; display: inline;" alt="clip_image038" src="http://lh5.ggpht.com/_vx0ImXMybTU/Sdk8G2z8UcI/AAAAAAAAAHs/JRyQwrZIiIE/clip_image038_thumb.jpg?imgmax=800" border="0" width="196" height="228" /&gt;&lt;/a&gt;&lt;/p&gt;This query produces physical SQL that has the same form we looked at earlier. It has a query block calculating UnitsSold by Product Description and Week. It has a query block joining the Product Table and the Dimension Table, qualifying the full outer join where Description = ‘Party’.&lt;br /&gt;&lt;p&gt;SELECT &lt;br /&gt;  max(T2616.One) AS c1&lt;br /&gt;, T2631.DESCRIPTION AS c2&lt;br /&gt;, T2637.WEEK AS c3&lt;br /&gt;FROM &lt;br /&gt;  FROSTINGWEEKS T2637 /* FrostingWeeksFBP */ &lt;br /&gt;, FROSTINGPRODUCTS T2631 /* FrostingProductsFBP */ &lt;br /&gt;,  (SELECT 1 One FROM Dual) T2616&lt;br /&gt;WHERE  ( T2631.DESCRIPTION = 'Party' ) &lt;br /&gt;GROUP BY &lt;br /&gt;  T2631.DESCRIPTION&lt;br /&gt;, T2637.WEEK &lt;br /&gt;HAVING max(T2616.One) = 1&lt;/p&gt;The SQL then outer joins the two result sets together, thereby preserving all the dimension values specified in the query filter.&lt;br /&gt;&lt;p&gt;Note: to display zeros for the weeks where values are NULL, I created a logical column using the ifnull function. In the database, the actual physical column is called QS (i.e. quantity sold). The logical column QS (i.e. Quantity Sold) is mapped directly to this physical column. The logical column UnitsSold is based on the logical column QS. &lt;/p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/Sdk8HBi1XyI/AAAAAAAAAHw/ZPSidcykmjE/s1600-h/clip_image040%5B3%5D.jpg"&gt;&lt;img title="clip_image040" style="border: 0px none ; display: inline;" alt="clip_image040" src="http://lh3.ggpht.com/_vx0ImXMybTU/Sdk8HvZHthI/AAAAAAAAAH0/oWlz2AdFRVM/clip_image040_thumb.jpg?imgmax=800" border="0" width="244" height="145" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/Sdk8H7XWQTI/AAAAAAAAAH4/bwQv_FF-oec/s1600-h/clip_image042%5B3%5D.jpg"&gt;&lt;img title="clip_image042" style="border: 0px none ; display: inline;" alt="clip_image042" src="http://lh4.ggpht.com/_vx0ImXMybTU/Sdk8IUCtaPI/AAAAAAAAAH8/Fs5ZYl8c2Tg/clip_image042_thumb.jpg?imgmax=800" border="0" width="239" height="194" /&gt;&lt;/a&gt;&lt;/p&gt;Now the Answers user can choose whether to display NULLs as zeros or not by selecting the appropriate measure, as well as whether to preserve dimensions or not by clicking on the saved filter.&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/Sdk8IlaPdDI/AAAAAAAAAIA/BQAYbK5guEc/s1600-h/clip_image044%5B3%5D.jpg"&gt;&lt;img title="clip_image044" style="border: 0px none ; display: inline;" alt="clip_image044" src="http://lh5.ggpht.com/_vx0ImXMybTU/Sdk8JNeXNQI/AAAAAAAAAIE/4TxZB45uEl8/clip_image044_thumb.jpg?imgmax=800" border="0" width="205" height="231" /&gt;&lt;/a&gt;&lt;/p&gt;An alternative to adding the ifnull function in the metadata would be to use it in the column formula on the Criteria tab in Answers.&lt;br /&gt;&lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/Sdk8JeNrk0I/AAAAAAAAAII/7V-HNjm3GdA/s1600-h/image%5B3%5D.png"&gt;&lt;img title="image" style="border: 0px none ; display: inline;" alt="image" src="http://lh6.ggpht.com/_vx0ImXMybTU/Sdk8KHjoR3I/AAAAAAAAAIM/x2cmkBFe9Nk/image_thumb%5B1%5D.png?imgmax=800" border="0" width="232" height="225" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;span style="font-weight: bold;"&gt;Note&lt;/span&gt;: on versions of OBIEE that included the Time Series functions Ago and ToDate prior to 10.1.3.3.2, this solution would have produced an error since complex joins from a period table source were not allowed. For these versions of OBIEE, the technique described here would have to be modified slightly by altering the Weeks table . Add a column to the Weeks table where all rows have the value of 1. Then join this “key” to the Dual table with a Key/Foreign Key physical join.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1481199104475905802-2073760799007460017?l=kpipartners.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/2073760799007460017'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/2073760799007460017'/><link rel='alternate' type='text/html' href='http://kpipartners.blogspot.com/2009/04/fact-based-partitioning.html' title='Fact-Based Partitioning'/><author><name>Kurt Wolff</name><uri>http://www.blogger.com/profile/07922051559586431987</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><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_vx0ImXMybTU/Sdk732x31BI/AAAAAAAAAFc/TJeYJSRxz50/s72-c/clip_image002_thumb.jpg?imgmax=800' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-1481199104475905802.post-2598237175090932388</id><published>2009-03-29T12:16:00.001-07:00</published><updated>2009-03-29T12:16:48.215-07:00</updated><title type='text'>Prompting for One Month – Displaying a Range of Months</title><content type='html'>&lt;p&gt;An OBIEE dashboard designer recently asked how to turn a prompt for a single month into a filter for a range of months. For example, if the user selects “Aug” in the prompt, the results should include not just August but also the three months preceding and following August (i.e. May through Nov).&lt;/p&gt;  &lt;p&gt;One way to do this is to take advantage of sequence numbers for months (giving an easy way to calculate a range of months) and presentation variables.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/Sc_I_oTZRGI/AAAAAAAAAEQ/lO1QTBcF-hw/s1600-h/clip_image002%5B3%5D.jpg"&gt;&lt;img title="clip_image002" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="206" alt="clip_image002" src="http://lh4.ggpht.com/_vx0ImXMybTU/Sc_JAdczcoI/AAAAAAAAAEU/2otZFVdSwTk/clip_image002_thumb.jpg?imgmax=800" width="244" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Here’s the prompt setup.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/Sc_JAuQju4I/AAAAAAAAAEY/YOiarIPr8PA/s1600-h/clip_image004%5B3%5D.jpg"&gt;&lt;img title="clip_image004" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="46" alt="clip_image004" src="http://lh6.ggpht.com/_vx0ImXMybTU/Sc_JBV8Z0CI/AAAAAAAAAEc/L8jRQBdklZo/clip_image004_thumb.jpg?imgmax=800" width="244" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Notice three things. The user’s selection of MonthNumber sets the value of a presentation variable called MoNum. Notice also that the formula for MonthNumber is not simply the column name itself but an expression: MonthNumber + 0. Finally, note that MonthNumber is constrained so that it is effectively set by the user’s choice of MonthName.&lt;/p&gt;  &lt;p&gt;For the query itself, the tricky part is setting the filter. There are two things to note here. The first is the use of “SQL Expressions” in the filter. The second is the syntax for the presentation variable in these SQL expressions.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/Sc_JBh0LBfI/AAAAAAAAAEg/6xCboJUq5kI/s1600-h/clip_image006%5B3%5D.jpg"&gt;&lt;img title="clip_image006" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="70" alt="clip_image006" src="http://lh3.ggpht.com/_vx0ImXMybTU/Sc_JCIZvoVI/AAAAAAAAAEk/brIhdTvblCU/clip_image006_thumb.jpg?imgmax=800" width="197" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/Sc_JCg5u1rI/AAAAAAAAAEo/-Xi1A09lnUU/s1600-h/clip_image008%5B3%5D.jpg"&gt;&lt;img title="clip_image008" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="131" alt="clip_image008" src="http://lh4.ggpht.com/_vx0ImXMybTU/Sc_JDOcQbrI/AAAAAAAAAEs/jfKkQ15O92w/clip_image008_thumb.jpg?imgmax=800" width="244" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Of course, instead of a hardcoded value of 3 in these expressions, you could also use another presentation variable. To illustrate, I changed the prompt to have an edit box that enables the user to set a presentation variable called “Range”. &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/Sc_JDzMEo3I/AAAAAAAAAEw/SHcpWsxG-wU/s1600-h/clip_image010%5B4%5D.jpg"&gt;&lt;img title="clip_image010" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="114" alt="clip_image010" src="http://lh4.ggpht.com/_vx0ImXMybTU/Sc_JEWzDPxI/AAAAAAAAAE0/f02rrxC5wyo/clip_image010_thumb%5B1%5D.jpg?imgmax=800" width="425" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Notice here that the “Column” is not a column at all but simply the number 1. To generate this, click any numeric column in the left panel, then change the formula to 1.&lt;/p&gt;  &lt;p&gt;On the dashboard, the prompt and query work together as shown here.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/Sc_JEh40wKI/AAAAAAAAAE4/_94Zgy-Sp80/s1600-h/clip_image012%5B3%5D.jpg"&gt;&lt;img title="clip_image012" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="189" alt="clip_image012" src="http://lh3.ggpht.com/_vx0ImXMybTU/Sc_JFC9nZ0I/AAAAAAAAAE8/fLBKROWEPJU/clip_image012_thumb.jpg?imgmax=800" width="244" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;The set up for the filter on the query includes this Range variable with a default value of 1. (This 1 is not connected at all to the fact that 1 was used in the formula in the prompt).&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/Sc_JFpzFROI/AAAAAAAAAFA/NcRHz2VKrPE/s1600-h/clip_image014%5B3%5D.jpg"&gt;&lt;img title="clip_image014" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="132" alt="clip_image014" src="http://lh5.ggpht.com/_vx0ImXMybTU/Sc_JGH3NxSI/AAAAAAAAAFE/e4M1yrv4jnE/clip_image014_thumb.jpg?imgmax=800" width="242" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Finally, you may be wondering why the formula for MonthNumber was set to MonthNumber+0 in the prompt. The reason is that if the formula in the prompt is the same as the formula for the column in the query, then the MonthNumber itself gets set as the filter. Here’s an example. The user selects May (month number 5) with a range of 2. The query shows the results for May only.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/Sc_JGaItFkI/AAAAAAAAAFI/sUwMRpWE_Vo/s1600-h/clip_image016%5B3%5D.jpg"&gt;&lt;img title="clip_image016" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="93" alt="clip_image016" src="http://lh5.ggpht.com/_vx0ImXMybTU/Sc_JG0M-TsI/AAAAAAAAAFM/0ef5viU-Lgk/clip_image016_thumb.jpg?imgmax=800" width="244" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Looking at how the query was modified by the prompt, you can see that the filter used just the value of MonthNumber in the prompt. The presentation variables were ignored.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/Sc_JHBaweZI/AAAAAAAAAFQ/abMtWHYSgt4/s1600-h/clip_image018%5B3%5D.jpg"&gt;&lt;img title="clip_image018" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="41" alt="clip_image018" src="http://lh5.ggpht.com/_vx0ImXMybTU/Sc_JHhFrUtI/AAAAAAAAAFU/p_sIOiZpXQg/clip_image018_thumb.jpg?imgmax=800" width="244" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1481199104475905802-2598237175090932388?l=kpipartners.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/2598237175090932388'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/2598237175090932388'/><link rel='alternate' type='text/html' href='http://kpipartners.blogspot.com/2009/03/prompting-for-one-month-displaying.html' title='Prompting for One Month – Displaying a Range of Months'/><author><name>Kurt Wolff</name><uri>http://www.blogger.com/profile/07922051559586431987</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><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_vx0ImXMybTU/Sc_JAdczcoI/AAAAAAAAAEU/2otZFVdSwTk/s72-c/clip_image002_thumb.jpg?imgmax=800' height='72' width='72'/></entry><entry><id>tag:blogger.com,1999:blog-1481199104475905802.post-1257560826852747162</id><published>2009-03-29T10:24:00.001-07:00</published><updated>2009-03-29T10:24:21.964-07:00</updated><title type='text'>Don’t Overstate Data</title><content type='html'>&lt;h3&gt;&amp;#160;&lt;/h3&gt;  &lt;p&gt;One of the tricky things for users of relational databases is forming queries without overstating (or understating) results as a consequence of table joins.&lt;/p&gt;  &lt;p&gt;Take this example that comes from an OBIEE user who had database tables containing information about opportunities and attachments, both of which related to accounts. The OppAcct table looked like this, showing that each account had multiple opportunities.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/Sc-ugfbStxI/AAAAAAAAACY/KUlV1bGGzQE/s1600-h/clip_image002%5B3%5D.jpg"&gt;&lt;img title="clip_image002" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="122" alt="clip_image002" src="http://lh6.ggpht.com/_vx0ImXMybTU/Sc-uhK7qMAI/AAAAAAAAACc/NCIg8vyq-fE/clip_image002_thumb.jpg?imgmax=800" width="233" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;The AcctAttachment table looked like this, showing that each account had multiple attachments.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/Sc-uhqO8CCI/AAAAAAAAACg/WHRg9mD5Lgs/s1600-h/clip_image004%5B3%5D.jpg"&gt;&lt;img title="clip_image004" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="188" alt="clip_image004" src="http://lh4.ggpht.com/_vx0ImXMybTU/Sc-uiaPv_xI/AAAAAAAAACk/132C1NmrACc/clip_image004_thumb.jpg?imgmax=800" width="194" border="0" /&gt;&lt;/a&gt;    &lt;br /&gt;This user wanted to be able to report on the data from these tables showing opportunities, attachments, and amounts for each account. Results would look like this for the BP account, achieved by a query that joined these two tables together on Account. &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/Sc-ujNOS_4I/AAAAAAAAACo/RhU3UH86dNE/s1600-h/clip_image006%5B3%5D.jpg"&gt;&lt;img title="clip_image006" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="90" alt="clip_image006" src="http://lh5.ggpht.com/_vx0ImXMybTU/Sc-ujjUrgaI/AAAAAAAAACs/otwFZ_mLFbg/clip_image006_thumb.jpg?imgmax=800" width="244" border="0" /&gt;&lt;/a&gt;    &lt;br /&gt;The user thought these results were OK and proceeded to model this data in OBIEE. Initial results looked good – just like her SQL query.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/Sc-ukZGdFbI/AAAAAAAAACw/rXUnXg6fb0E/s1600-h/clip_image008%5B3%5D.jpg"&gt;&lt;img title="clip_image008" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="151" alt="clip_image008" src="http://lh4.ggpht.com/_vx0ImXMybTU/Sc-ukwGtm5I/AAAAAAAAAC0/BbgwgKM74FU/clip_image008_thumb.jpg?imgmax=800" width="235" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;However, when she removed Attachment and OpptyID columns from the query, Answers overstated results by a factor of 2.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/Sc-ulbwKLQI/AAAAAAAAAC4/Xp3JvlmrLPI/s1600-h/clip_image010%5B3%5D.jpg"&gt;&lt;img title="clip_image010" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="124" alt="clip_image010" src="http://lh3.ggpht.com/_vx0ImXMybTU/Sc-ul-osMwI/AAAAAAAAAC8/GuR6cKPxyk8/clip_image010_thumb.jpg?imgmax=800" width="186" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/Sc-umXufHRI/AAAAAAAAADA/eyiyHR_HDdU/s1600-h/clip_image012%5B3%5D.jpg"&gt;&lt;img title="clip_image012" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="102" alt="clip_image012" src="http://lh6.ggpht.com/_vx0ImXMybTU/Sc-unDYya0I/AAAAAAAAADE/dxd5gBinAaM/clip_image012_thumb.jpg?imgmax=800" width="181" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;What's gone wrong here? There are two possible sources of the problem: the data modeling in the database and the metadata modeling in OBIEE. We will look at both.&lt;/p&gt;  &lt;p&gt;In the database model, there isn't a source of the distinct set of accounts. Someone familiar with dimensional modeling would phrase it differently: there isn't an account dimension table. Using the AccountAttachment table as a source of accounts leads to overstatement because each account may have several entries in this table. BP, in fact, has two, which leads to the doubling of the amounts for each opportunity when this table joins to the OppAcct table.&lt;/p&gt;  &lt;p&gt;The user made these tables sources in her OBIEE business model (the “middle layer” in the OBIEE administration tool). She knew that an OBIEE business model requires, at a minimum, two logical tables (a logical dimension table and a logical fact table). Since the only aggregatable column is Amount, OppAccount was the best choice for the logical fact table source. She made AcctAttachment the logical dimension table source, since it had no aggregatable columns. She was following basic principles correctly.&lt;/p&gt;  &lt;p&gt;Dimension table sources need to join to fact table sources. She created a physical join on AccountAttachment.Account = OppAcct.Account. Making this a key/foreign key join involved “lying” to the OBIEE administration tool about what the key of the AccountAttachment table is. It isn't Account, since the same Account exists multiple times in the table. And lying about dimension table keys is a good indication that trouble lies ahead.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/Sc-un6McKBI/AAAAAAAAADI/vT6xibj5KCs/s1600-h/clip_image014%5B3%5D.jpg"&gt;&lt;img title="clip_image014" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="160" alt="clip_image014" src="http://lh5.ggpht.com/_vx0ImXMybTU/Sc-uoi9nwOI/AAAAAAAAADM/WUiEGpdSOq4/clip_image014_thumb.jpg?imgmax=800" width="244" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;In the business model, the administration tool requires that every logical dimension table has a logical key. This was another opportunity to lie, and lies here are another portent trouble.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/Sc-upPzs9iI/AAAAAAAAADQ/muff9MclFbk/s1600-h/clip_image016%5B3%5D.jpg"&gt;&lt;img title="clip_image016" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="212" alt="clip_image016" src="http://lh5.ggpht.com/_vx0ImXMybTU/Sc-up6DKhmI/AAAAAAAAADU/a2EP52yszos/clip_image016_thumb.jpg?imgmax=800" width="244" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Suppose she didn't lie here specified that the logical table key consists of Account plus Attachment. When doing a consistency check, she would have seen this Warning. &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/Sc-uqWuDbcI/AAAAAAAAADY/tiwpCRy5Cmw/s1600-h/clip_image018%5B3%5D.jpg"&gt;&lt;img title="clip_image018" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="40" alt="clip_image018" src="http://lh5.ggpht.com/_vx0ImXMybTU/Sc-uqx06AYI/AAAAAAAAADc/upTCT9_zc7A/clip_image018_thumb.jpg?imgmax=800" width="244" border="0" /&gt;&lt;/a&gt;    &lt;br /&gt;Obviously, the two tables do join. So the warning is misleading. What the administration tool is really warning you about is that the columns defined as the logical key do not match the columns in the physical join between these sources.&lt;/p&gt;  &lt;p&gt;One way to solve this problem is to introduce a source of distinct accounts. You could do this in the database, and of course that's the best way, but you could also (for relatively small data sets) introduce a SQL-based table in the physical layer of the metadata.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_vx0ImXMybTU/Sc-usASfjTI/AAAAAAAAADg/w4iKhrBrPMY/s1600-h/clip_image020%5B3%5D.jpg"&gt;&lt;img title="clip_image020" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="206" alt="clip_image020" src="http://lh6.ggpht.com/_vx0ImXMybTU/Sc-usvvUTsI/AAAAAAAAADk/J_PbzNDV1TY/clip_image020_thumb.jpg?imgmax=800" width="244" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;You could join this “table” to the other two data sources – and now there would be no reason to lie about joins or keys.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_vx0ImXMybTU/Sc-utFJ6XlI/AAAAAAAAADo/FNIQbP4GsTw/s1600-h/clip_image022%5B3%5D.jpg"&gt;&lt;img title="clip_image022" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="143" alt="clip_image022" src="http://lh6.ggpht.com/_vx0ImXMybTU/Sc-ut2h_JcI/AAAAAAAAADs/k-d9TbQZR9g/clip_image022_thumb.jpg?imgmax=800" width="242" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;The SQL table now is usable as a source in the business model.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/Sc-uuVzoyNI/AAAAAAAAADw/QOV1z53wPe8/s1600-h/clip_image024%5B3%5D.jpg"&gt;&lt;img title="clip_image024" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="186" alt="clip_image024" src="http://lh5.ggpht.com/_vx0ImXMybTU/Sc-uu4r2jRI/AAAAAAAAAD0/1LpM1JiR6mo/clip_image024_thumb.jpg?imgmax=800" width="199" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Now aggregations of Amount will be correct, as the BI Server will drop the AcctAttachment table out of the physical query when it is not needed.   &lt;table cellspacing="0" cellpadding="0"&gt;&lt;tbody&gt;       &lt;tr&gt;         &lt;td width="19"&gt;&lt;/td&gt;          &lt;td width="209"&gt;&lt;/td&gt;          &lt;td width="10"&gt;&lt;/td&gt;          &lt;td width="142"&gt;&lt;/td&gt;          &lt;td width="21"&gt;&lt;/td&gt;          &lt;td width="107"&gt;&lt;/td&gt;       &lt;/tr&gt;        &lt;tr&gt;         &lt;td&gt;&lt;/td&gt;          &lt;td valign="top"&gt;&lt;a href="http://lh4.ggpht.com/_vx0ImXMybTU/Sc-uvZYjkWI/AAAAAAAAAD4/bzQwd8kkduY/s1600-h/clip_image028%5B3%5D.jpg"&gt;&lt;img title="clip_image028" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="115" alt="clip_image028" src="http://lh5.ggpht.com/_vx0ImXMybTU/Sc-uv1sev_I/AAAAAAAAAD8/xmgfUz5l66M/clip_image028_thumb.jpg?imgmax=800" width="213" border="0" /&gt;&lt;/a&gt;&lt;/td&gt;          &lt;td&gt;&lt;/td&gt;          &lt;td valign="top"&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/Sc-uwQIo-TI/AAAAAAAAAEA/fNgovlLyHKc/s1600-h/clip_image029%5B3%5D.jpg"&gt;&lt;img title="clip_image029" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="74" alt="clip_image029" src="http://lh4.ggpht.com/_vx0ImXMybTU/Sc-uwkcmlvI/AAAAAAAAAEE/J3G4oiGwOqA/clip_image029_thumb.jpg?imgmax=800" width="146" border="0" /&gt;&lt;/a&gt;&lt;/td&gt;          &lt;td&gt;&lt;/td&gt;          &lt;td valign="top"&gt;&lt;a href="http://lh5.ggpht.com/_vx0ImXMybTU/Sc-uwwxaZYI/AAAAAAAAAEI/pUtvKg2njPI/s1600-h/clip_image030%5B3%5D.jpg"&gt;&lt;img title="clip_image030" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="61" alt="clip_image030" src="http://lh5.ggpht.com/_vx0ImXMybTU/Sc-uxTXUiNI/AAAAAAAAAEM/nAd3twgm7gg/clip_image030_thumb.jpg?imgmax=800" width="111" border="0" /&gt;&lt;/a&gt;&lt;/td&gt;       &lt;/tr&gt;        &lt;tr&gt;         &lt;td&gt;&lt;/td&gt;       &lt;/tr&gt;        &lt;tr&gt;         &lt;td&gt;&lt;/td&gt;       &lt;/tr&gt;     &lt;/tbody&gt;&lt;/table&gt; &lt;/p&gt;  &lt;p&gt;This is a very simple example, of course, but it illustrates some fundamental principles for successful OBIEE projects. First of all, develop a dimensionally sound physical data model. Time and effort expended in developing a data model for BI is a sound investment. Whenever an OBIEE project seems to get overly complex, it is a good indication that the underlying data model is not optimal. Secondly, be careful to tell the truth to the administration tool. The admin tool may allow you to get away with it, and some queries may even be correct. But more often than not, trouble lies ahead.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1481199104475905802-1257560826852747162?l=kpipartners.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/1257560826852747162'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1481199104475905802/posts/default/1257560826852747162'/><link rel='alternate' type='text/html' href='http://kpipartners.blogspot.com/2009/03/dont-overstate-data.html' title='Don’t Overstate Data'/><author><name>Kurt Wolff</name><uri>http://www.blogger.com/profile/07922051559586431987</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><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/_vx0ImXMybTU/Sc-uhK7qMAI/AAAAAAAAACc/NCIg8vyq-fE/s72-c/clip_image002_thumb.jpg?imgmax=800' height='72' width='72'/></entry></feed>
