tag:blogger.com,1999:blog-85021426275998009282024-03-20T23:02:46.807-07:00Best Way To Understand BI and Data Visualization And How Everything Works HereLearn BI and visualization with methods tailored for beginners. To understand the concept, technology and implementation of business intelligence reporting and visualization is easy if you have the right resource and the right roadmap. Everything we do using any BI and Visualization tool becomes easy once we see the Insight of it clearly.Bojiehttp://www.blogger.com/profile/13163354520521690612noreply@blogger.comBlogger112125tag:blogger.com,1999:blog-8502142627599800928.post-16354735007836541872016-05-03T22:31:00.001-07:002016-05-03T22:31:04.154-07:00Get Last Business Day of each month using OBIEEHello<br />
<br />
Today I want to share with you a technique that I use to intelligently get last business day of each month in OBIEE taking accounts of holidays, this technique is useful when all you have is just a basic time dimensions with year, month and date to start with:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgd2rHvZLKxEDHWrWW5cOid7Q3V-lokA_t8kDgdi8TtH6x1rrQehpMZRpjJnKyoK_jTBQI4iIYV93EIHYVD9Aw0qmaRkODdAvYpY2nVZdbtzSByLZyNbf_KkxwpdqA6SFdGLciwMenw-L8/s1600/Last+Business+Date1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgd2rHvZLKxEDHWrWW5cOid7Q3V-lokA_t8kDgdi8TtH6x1rrQehpMZRpjJnKyoK_jTBQI4iIYV93EIHYVD9Aw0qmaRkODdAvYpY2nVZdbtzSByLZyNbf_KkxwpdqA6SFdGLciwMenw-L8/s400/Last+Business+Date1.jpg" width="147" /></a></div>
<br />
Now, the first step is to identify all of the day number of each months and the maximum day number of each month. OBIEE has native function to get you the day number in a month.<br />
<br />
So I am going to create 3 columns:<br />
<br />
Date number: dayofmonth(date)<br />
Last Day of the month: Max(Dayofmonth(date) by year, month)<br />
Condition: Compare Date number and Last Day of the month, if they are equal, then 1, else 0<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiMjaKMB2OxzA7GgxrYkvYQzdLxMMbrX2pgbYwuqS_Vbqq2rY6E9K1Rk15fm_RrIW9wsyn3KiI0LwB9C_LT8J1z7RSPDqAT7Ai6XXv-XGoEZYqcMo5QWTRgBOo7TF3gijrR9LJKdH5uggw/s1600/Last+Business+Date+2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="448" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiMjaKMB2OxzA7GgxrYkvYQzdLxMMbrX2pgbYwuqS_Vbqq2rY6E9K1Rk15fm_RrIW9wsyn3KiI0LwB9C_LT8J1z7RSPDqAT7Ai6XXv-XGoEZYqcMo5QWTRgBOo7TF3gijrR9LJKdH5uggw/s640/Last+Business+Date+2.jpg" width="640" /></a></div>
<br />
The result of the above report will look like the following, and the row that's highlighted is going to be the last day of each month, because the maximum date number in Jan is always 31 and it happens to be equal last day of the month for THIS ROW.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPRK7RZeb01LM3O5zY_FGMm9CaAekpkkE8xd4spAl_I6EHcUouVotR_E7vOUIT95TRPDhdt1xeb8bD1_K8ZJR7GQOGuFOCyAL1QH6beBzmSXOuMGB_eXbCGu-m9CurVjHb6zEXZYUz0NI/s1600/Last+Business+Date+3.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPRK7RZeb01LM3O5zY_FGMm9CaAekpkkE8xd4spAl_I6EHcUouVotR_E7vOUIT95TRPDhdt1xeb8bD1_K8ZJR7GQOGuFOCyAL1QH6beBzmSXOuMGB_eXbCGu-m9CurVjHb6zEXZYUz0NI/s400/Last+Business+Date+3.jpg" width="301" /></a></div>
<br />
Now, simply apply a filter where condition = 1, then the report will always give you last day of every month:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTGakovftugntoVahZFpVxQyTPTyXG4UPdxhU9gR-y7w_23QtZnl7Aaba9nF2YN0DXbz7FU-UK89uBo5dmUO5Kpbyvf5im8bC6P3yYYP47QxtbnwFrbVfWU0W5dWaDGi95_P2VVx-cDF8/s1600/Last+Business+Date+4.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="302" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTGakovftugntoVahZFpVxQyTPTyXG4UPdxhU9gR-y7w_23QtZnl7Aaba9nF2YN0DXbz7FU-UK89uBo5dmUO5Kpbyvf5im8bC6P3yYYP47QxtbnwFrbVfWU0W5dWaDGi95_P2VVx-cDF8/s640/Last+Business+Date+4.jpg" width="640" /></a></div>
<br />
Now that we have the last day of the month, we now need to determine whether that happens to be the business date or not. So if it falls on saturday, then the business day has to be one day prior; if Sunday, then 2 days prior; if on Monday and holiday, then maybe 3 days prior depending on your company's rules. So we can program these things using case statement with timestampadd.<br />
<br />
First, add another 3 columns:<br />
<br />
Last Business Day: This converts day into day names, such as Monday, Tuesday, Friday etc.<br />
Last Business Day Case: This is where the main logic of determining what's business day is created.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiVgugcF5562-zFFdHcJHep_Y0CQ_29AtdKju7cngxx6q43I4k53ouyCSNr_yh0YJRJV38v4avWK2LEwHt7PNkyTVc_RArXWG7EJd41FTsmTMMIGP0YAJN4lCLZ5DrBsWqxL5bpL2pczqM/s1600/Last+Business+Date+5.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiVgugcF5562-zFFdHcJHep_Y0CQ_29AtdKju7cngxx6q43I4k53ouyCSNr_yh0YJRJV38v4avWK2LEwHt7PNkyTVc_RArXWG7EJd41FTsmTMMIGP0YAJN4lCLZ5DrBsWqxL5bpL2pczqM/s640/Last+Business+Date+5.jpg" width="640" /></a></div>
<br />
<br />
Here in my company, the rules are pretty simple for holidays. If the last Monday of May happens to be May 31st, which is memorial day, then last business day of May of that year should be 3 days prior; if 12/31 falls on Tuesday thru Saturday, then last business day should be 1 day prior; if Sunday, then 2 days; if Monday then 3 days. The rest, just weekend scenarios, which is either minus 1 day or 2 days.<br />
<br />
So my formula is the following:<br />
<span style="font-size: x-small;">case when DAYNAME("Time"."Date") = 'Sun' THEN TIMESTAMPADD(SQL_TSI_DAY, -2 , "Time"."Date") when DAYNAME("Time"."Date") = </span><br />
<span style="font-size: x-small;"><br /></span>
<span style="font-size: x-small;">'Sat' then TIMESTAMPADD(SQL_TSI_DAY, -1 , "Time"."Date") when DAYNAME("Time"."Date") in ('Sat','Fri', 'Thu', 'Wed', 'Tue') </span><br />
<span style="font-size: x-small;"><br /></span>
<span style="font-size: x-small;">and "Time"."Month" = 'Dec' then TIMESTAMPADD(SQL_TSI_DAY, -1 , "Time"."Date") when DAYNAME("Time"."Date") = 'Sun' and </span><br />
<span style="font-size: x-small;"><br /></span>
<span style="font-size: x-small;">"Time"."Month" = 'Dec' then TIMESTAMPADD(SQL_TSI_DAY, -2 , "Time"."Date") when DAYNAME("Time"."Date") = 'Mon' and </span><br />
<span style="font-size: x-small;"><br /></span>
<span style="font-size: x-small;">"Time"."Month" = 'Dec' then TIMESTAMPADD(SQL_TSI_DAY, -3 , "Time"."Date") when "Time"."Month" = 'May' and DAYNAME</span><br />
<span style="font-size: x-small;"><br /></span>
<span style="font-size: x-small;">("Time"."Date") = 'Mon' then TIMESTAMPADD(SQL_TSI_DAY, -3 , "Time"."Date")</span><br />
<span style="font-size: x-small;">else "Time"."Date" end</span><br />
<span style="font-size: x-small;"><br /></span>
Now for the use case. The user want to be able to select any integer from 1 to 12, and the report should show data for the last business days of each month for the last 1 to 12 months including the date the user pass from the prompt.<br />
<br />
Therefore, This report has to have a filter on date, which is Date <= @{Date}. This not only accepts the user input date, but also allows the report the show all of the past days too.<br />
<br />
Add another column that Rank dates: Rank(date). This column will return integer as a ranking number from high to low.<br />
<br />
More importantly, apply a filter on rank(date) column:<br />
Rank(date) <= @{N}. This will allow the report to run for N number of past month's last business date based on user inputs.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgwtOSydKdAFlpRi7Q-7IX5Crq9KYSNhuEz6UaNSXTK_DQXiF4wQ-lNTZy7YACa4csT2Dmbn6Qgjyn2R_nMLd1MZLJLqUaoKY4hGvHEf4ZKcsY-0FoiH7sFHTXLWeyBjVTELRvmDGIbyd4/s1600/Last+Business+Date+6.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="238" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgwtOSydKdAFlpRi7Q-7IX5Crq9KYSNhuEz6UaNSXTK_DQXiF4wQ-lNTZy7YACa4csT2Dmbn6Qgjyn2R_nMLd1MZLJLqUaoKY4hGvHEf4ZKcsY-0FoiH7sFHTXLWeyBjVTELRvmDGIbyd4/s640/Last+Business+Date+6.jpg" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Now lets test the report. As you can see, my default user input date is 5/2/2016, and default number is 6. Now I am getting 5/2/2016 as well as 5 other dates in the past that happens to be past month's last business date.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgXUHf1Pmb3P9uG7rvcXQFDFYMYPUxm6NjdL1NUtgSNV3BglSJ9aJVAS7XEK-fIvNkGBV-r7dRPHp_NGD_hbHOebstTHolkD053LelZIlOvNLmTfORf4LbIPbsdTR80yBqrQkjycfwS5xY/s1600/Last+Business+Date+7.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="312" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgXUHf1Pmb3P9uG7rvcXQFDFYMYPUxm6NjdL1NUtgSNV3BglSJ9aJVAS7XEK-fIvNkGBV-r7dRPHp_NGD_hbHOebstTHolkD053LelZIlOvNLmTfORf4LbIPbsdTR80yBqrQkjycfwS5xY/s640/Last+Business+Date+7.jpg" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
An actual report may look at this as below where the user select dates, period level and number of period on the left and he report shows last N months or week's of last business date from user selected date:</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiqpQaYF2-bKjjMchR4c-gRfAAnqZ_4FD0RzfpYGGfp6QgJvlIT2IK6r5Ur9M2ywcWhBHXOfdHmP-7uIGprvKyov4qzKEDnvXtzD98YWY86AWDNPJ-WTstY5bzSjEKq7JqsSY-PmEwepPk/s1600/Last+Business+Date+8.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="418" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiqpQaYF2-bKjjMchR4c-gRfAAnqZ_4FD0RzfpYGGfp6QgJvlIT2IK6r5Ur9M2ywcWhBHXOfdHmP-7uIGprvKyov4qzKEDnvXtzD98YWY86AWDNPJ-WTstY5bzSjEKq7JqsSY-PmEwepPk/s640/Last+Business+Date+8.jpg" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Thanks, until next time</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
Bojiehttp://www.blogger.com/profile/13163354520521690612noreply@blogger.com1tag:blogger.com,1999:blog-8502142627599800928.post-52849393058729298332015-02-26T15:54:00.000-08:002015-02-26T15:54:00.137-08:00Learning python for BI Hello All<br />
<br />
What does Python have to do with BI report? Well maybe no much directly in enterprise environment, but python can be very useful for my garage project. I want to build a Data visualization product that shows real time update of certain market driven products and get users to subscribe to it for a monthly fee, that's what python can be handy for my ETL process.<br />
<br />
Just to give an idea, I may choose to store my data in one of these open source DBs like Hadoop Hive, Cassandra or Mongo DB, and I can use python to pull data from whatever source that i want, for example zillow.com, if i want to analyze all of the real estate properties in America. For visualization, i can use Tableau to display the market trend and my analysis..<br />
<br />
Now you may think that my idea sucks and there are so many real estate websites out there already. Well of course the idea sucks because this is not what I am really trying to explain you here, if i had a great idea, I wouldn't be letting it out until i secure my billion dollar patent first. Duh..<br />
<br />
Anyways, Python can be a very powerful yet quite sample programming languages that supports various DB commends (of course, much more than just doing things with DB), so it's worth your time to look into it if you are a techy guy.<br />
<br />
Get good at it and you can write your own ETL tool.<br />
<br />
A good place to start if you have very little experience is here:<br />
http://learnpythonthehardway.org/book/<br />
<br />
Thanks<br />
<br />
Until next timeBojiehttp://www.blogger.com/profile/13163354520521690612noreply@blogger.com6tag:blogger.com,1999:blog-8502142627599800928.post-91951180158830642092015-02-23T15:41:00.000-08:002015-02-23T15:41:00.099-08:00Tableau Visualization -- How to build Waterfall chartWaterfall chart can be used in accounting to show trends in the horizontal time span usually as result of positive and negative influences in sequential order. We will look at the product's profit over years.<br />
<br />
To start, lets have profit in row and product and time in column:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhfspajmBxr0VQvcgCI0N87g17rTEToP6D8uDNGMWsrWRuckTVxtwrsMeMIjj2PmsOuY33hWX3uwfiDoLn1B5GMijil1q17sFyMCxtYYygqmto8EXLZ2Pp58TFi5zzgZIUzrkP8FSYMky0/s1600/1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhfspajmBxr0VQvcgCI0N87g17rTEToP6D8uDNGMWsrWRuckTVxtwrsMeMIjj2PmsOuY33hWX3uwfiDoLn1B5GMijil1q17sFyMCxtYYygqmto8EXLZ2Pp58TFi5zzgZIUzrkP8FSYMky0/s1600/1.jpg" height="360" width="640" /></a></div>
<br />
As you can see, some product creates positive profit and some creates negative profit.<br />
<br />
Now let's add a new table calculation off profit to get the running total of the profit:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgoclJ20oabIvkNUXr29JWGYywoo3XQUjNDlBlhjsALGBd8RdgL-LgBkJh0AGqHWVf4rToe68K_bBwGFYG7I_2SXxj_lK85ZYht0WS9-oED8pvdQwQBDu8HWL1orP4nWIwKUUm3lAl9yBc/s1600/2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgoclJ20oabIvkNUXr29JWGYywoo3XQUjNDlBlhjsALGBd8RdgL-LgBkJh0AGqHWVf4rToe68K_bBwGFYG7I_2SXxj_lK85ZYht0WS9-oED8pvdQwQBDu8HWL1orP4nWIwKUUm3lAl9yBc/s1600/2.jpg" height="360" width="640" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgve9mnruBsIqrZCoHzbJ3TIcEdnjvpl8QhQm_g88Xf-I9CLFjVwVBP9oQ2ZOG4SBxo-ZFMmxk8pdlYb_X7rVQR6dZ8s-tTeyqWXSUp5YKFk2C2ERjEITF8U4DtLQ9PjGZI8H9IILrBV1Q/s1600/3.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgve9mnruBsIqrZCoHzbJ3TIcEdnjvpl8QhQm_g88Xf-I9CLFjVwVBP9oQ2ZOG4SBxo-ZFMmxk8pdlYb_X7rVQR6dZ8s-tTeyqWXSUp5YKFk2C2ERjEITF8U4DtLQ9PjGZI8H9IILrBV1Q/s1600/3.jpg" height="360" width="640" /></a></div>
<br />
Now the chart looks like this:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiydP4xjNztV3oWvd29tFmVJgNmx-xcCydy7pSK718co46BPDMTX90HOVsaOl4Qc79eixfOimnepnN5FuNf1kVeNfP9yLTA9k6C9q9zOOsTTmoI9uoC2Sug2R9fHXOWAeXuGdGlGr4FHPM/s1600/4.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiydP4xjNztV3oWvd29tFmVJgNmx-xcCydy7pSK718co46BPDMTX90HOVsaOl4Qc79eixfOimnepnN5FuNf1kVeNfP9yLTA9k6C9q9zOOsTTmoI9uoC2Sug2R9fHXOWAeXuGdGlGr4FHPM/s1600/4.jpg" height="360" width="640" /></a></div>
<br />
<br />
We want to distinguish the negative from the positive. To do that we create condition on profit by adding a new calculated field ('profitable ?') with the following formula: SUM([Profit])>=0<br />
<br />
This will create a binary output: greater than 0 or less than 0<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhfveg4yCJIper_DkCsAZ9g67kSrLVGeVFMVJ8bLCuVsFRVTuPDZD1FCVpWwLLOBwR8HzYvjbfCRZgQIAT4sHhztmfYA6QxWi0JikA9pWNNPDjXGtX7ERcrVfE_6umUoRN6dLS6a1F6njM/s1600/5.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhfveg4yCJIper_DkCsAZ9g67kSrLVGeVFMVJ8bLCuVsFRVTuPDZD1FCVpWwLLOBwR8HzYvjbfCRZgQIAT4sHhztmfYA6QxWi0JikA9pWNNPDjXGtX7ERcrVfE_6umUoRN6dLS6a1F6njM/s1600/5.jpg" height="360" width="640" /></a></div>
<br />
<br />
Putting 'Profitable ?' into the color windows, the chart is colored:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgoFNqxc86KYt_llBMVZp9C1bdgUnEZnbZ2xQOOyGTWjCO_B7KTCk6cQiZXU6mUfdvKgJI3cEq24LQQD6vOLvNG_sG1xQmEoqKgt_8k6t0Gldm8UGjOLwxi3XTkqFyJOgZO_D261IFkw2w/s1600/6.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgoFNqxc86KYt_llBMVZp9C1bdgUnEZnbZ2xQOOyGTWjCO_B7KTCk6cQiZXU6mUfdvKgJI3cEq24LQQD6vOLvNG_sG1xQmEoqKgt_8k6t0Gldm8UGjOLwxi3XTkqFyJOgZO_D261IFkw2w/s1600/6.jpg" height="360" width="640" /></a></div>
<br />
<br />
Now change the view from automatic to ''Gnatt" and the chart changes:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-MB08FSdSRxiCwuP83Wo5CmWxwZ_S02csJIRIzYTSvXKpodA0Pm5SkdSfxr2YcX0p30pCTTqjQoGA9nu6r4Jp88KqwOsLSrZEoS63csdQ4yqAlRw4BmfBVRXw52mfbTplRVFlqfQA-o0/s1600/7.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-MB08FSdSRxiCwuP83Wo5CmWxwZ_S02csJIRIzYTSvXKpodA0Pm5SkdSfxr2YcX0p30pCTTqjQoGA9nu6r4Jp88KqwOsLSrZEoS63csdQ4yqAlRw4BmfBVRXw52mfbTplRVFlqfQA-o0/s1600/7.jpg" height="360" width="640" /></a></div>
<br />
<br />
Put profit into size windows so that all of the Gnatt size will change based on the profit amount:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7jIQvsa8M3LCcwU5QzNn6skZ79IKGZVvcpJFNZRp-JyRhzcfuikF0i6zWOUEY11qVjd9jFimqEYgg7yMrVg2aykgK7j2fsB3Pzvr4asRPjTXOLRy8iapCqEq42KIDb6MEJj6z7Pd6TKw/s1600/8.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7jIQvsa8M3LCcwU5QzNn6skZ79IKGZVvcpJFNZRp-JyRhzcfuikF0i6zWOUEY11qVjd9jFimqEYgg7yMrVg2aykgK7j2fsB3Pzvr4asRPjTXOLRy8iapCqEq42KIDb6MEJj6z7Pd6TKw/s1600/8.jpg" height="360" width="640" /></a></div>
<br />
Now you might notice that all of the blue color Gnatt bars are growing downwards while the yellow ones are growing upwards. This is because the blue color happens to represent negative profits. Some user maybe okay with the way this chart looks, while others may prefer to flip the blue bars and make it go upwards just like the rest. To do so, let's create another calculated field call 'Negative profit' with the following formula:<br />
-[Profit]<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggIOAuJ0vGGwgPZ1s0TRZHkGYFO3P58Sng8QH-z5aODZUkS5KdEJDml2cHXPsrtHziwse8mMYwlwIWm0vHFFdL7FGFu6v7v-HTPKol4kX461PtD0uCymT0bqeE35k-q-X6Sz69b_oKFP4/s1600/9.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggIOAuJ0vGGwgPZ1s0TRZHkGYFO3P58Sng8QH-z5aODZUkS5KdEJDml2cHXPsrtHziwse8mMYwlwIWm0vHFFdL7FGFu6v7v-HTPKol4kX461PtD0uCymT0bqeE35k-q-X6Sz69b_oKFP4/s1600/9.jpg" height="360" width="640" /></a></div>
<br />
<br />
<br />
Put 'negative profit' into size windows and now all of the bars are facing the same direction:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiMYvsHqYIvdQ8OKIvU8sTTlGL4UWock5O2t-fKhYiBJdzSeY-0Q2PzC4dexmyU-MJPdB2RszM1RayseylGsDeaTQNiW67bCNzs_fMC9chvfYwOrdOkc9ZYao9O5yQD13wGSj6OXopI7Fo/s1600/10.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiMYvsHqYIvdQ8OKIvU8sTTlGL4UWock5O2t-fKhYiBJdzSeY-0Q2PzC4dexmyU-MJPdB2RszM1RayseylGsDeaTQNiW67bCNzs_fMC9chvfYwOrdOkc9ZYao9O5yQD13wGSj6OXopI7Fo/s1600/10.jpg" height="360" width="640" /></a></div>
<br />
<br />
Thanks<br />
<br />
Until next timeBojiehttp://www.blogger.com/profile/13163354520521690612noreply@blogger.com3tag:blogger.com,1999:blog-8502142627599800928.post-71344798893797929432015-02-19T14:28:00.000-08:002015-02-19T14:28:00.427-08:00OBIA Financial analytic AP Process 101Today we are looking at the basic process of AP or Accounts Payable in Oracle EBS for financial analytic. This is to give you a basic understanding of how AP process works for most companies that implements Oracle EBS. In order to build AP reports out of EBS source, you need to understand the basics.<br />
<br />
Although different companies will have different accounting process and different product dealings, the basic AP process is comparable.<br />
<br />
Based on the below diagram, the AP process includes 4 main blocks:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLcT8jS17zWgOTk74A0eGnuTdHjr-VUv6MEqSc9spFwyHZTx0TxAlQBfTLv1m3-FaFbEXXu0CIwKLLxSNewV_1QBi4n7I46-BZCDUmpiZpHpHOPhHGX9UNJfHSHT7LhFXFBhQSQgCV0MY/s1600/AP1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLcT8jS17zWgOTk74A0eGnuTdHjr-VUv6MEqSc9spFwyHZTx0TxAlQBfTLv1m3-FaFbEXXu0CIwKLLxSNewV_1QBi4n7I46-BZCDUmpiZpHpHOPhHGX9UNJfHSHT7LhFXFBhQSQgCV0MY/s1600/AP1.jpg" height="240" width="640" /></a></div>
<br />
Purchase Orders:<br />
<br />
These are the purchase orders that the company has decided to execute through Oracle IProcurement. It basically means that the company has decided to buy, this can include items for the office, materials that the company products need, the contract for consultants, laptops, speakers, insurance services or electricity and gas.<br />
<br />
PO usually consist of 3 hierarchies:<br />
Header - highest level.<br />
Line - under each header, there can be multiple lines.<br />
Distribution - Under each line ordered, it can be used for different locations or cost centers. Manage A from this department uses it for a few weeks and then Manager B from another department uses it. From accounting perspective, it may belong to different cost centers<br />
<br />
Invoices:<br />
This is when the invoices of these PO have been entered. They have mirror the hierarchical structure as Purchase order. Each of the 3 AP Invoice tables can join to the corresponding PO tables.<br />
<br />
Receives<br />
Now that the company has been invoiced by vendors, they will receive goods and services that they purchase. The act of receiving is recorded in RCV_TRANSACTIONS table as transactions. Note that receives are at invoice line level. If your company makes a lot of order from one vendor and the goods will be delivered throughout a period of time continuously, you may want to check the Receipt_Date and take the Max of this date for each transaction, or it could potentially influence the grain of each invoices. As an example, most of the time you are interested in seeing how much you have totally spent in your trip to the local grocery store but not necessarily interested at each items you purchase, or at the end of a period, how many chairs does your company receive rather than how many chairs received on each day.<br />
<br />
Hold<br />
Hold indicates the status of your company's decision on handling the invoices. Let's say after receiving the invoices, the department realized that it doesn't have all of the goods that they thought they ordered. This could be categorized into 2 main reasons:<br />
<br />
1. The company makes a mistake in dealing with the transactions. Maybe the goods are delivered to the wrong department or miscounted. The error lies within the company, therefore they are still responsible for making the payment on time according the to invoice.<br />
<br />
2. The company thinks that the vendor has not completely delivered their services according to the PO. The order says 100 laptops but the company only receives 95, therefore the company puts this invoice on hold and waiting for the delivery of the remaining 5.<br />
<br />
These information will be stored in AP_INVOICE_HOLD_DETAIL table and HOLD_TYPE will indicate what type of hold this invoice is. For each Invoice_ID, there can be multiple HOLD_LOOKUP_CODE to tell you the specific reasons for being on hold. <br />
<br />
Now if the hold is deemed to be vendor not fulfilling its responsibility, the HOLD_RELEASE_DATE column will be used for the invoice payment due date, that is, until the error has been corrected, the hold will be released.<br />
<br />
The payment_date and due_date are stored in various tables, but AP_PAYMENTS_ALL, which is the block at the bottom of the 4 blocks in the diagram, generally stores this information.<br />
<br />
So for accounting purposes, the payment date and due date are based on HOLD conditions.<br />
<br />
The process of determining what HOLD condition is, or whether there should be a hold or not, the called 3-way match, which means you have to compare the PO to invoice to the receipts to see if they matches or not.<br />
<br />
If there is reporting requirements that needs OBIEE report that does these kind of comparisons, you should already know the logic and the type of report that will make sense.<br />
<br />
Thank you<br />
<br />
Until next time<br />
<br />
<br />Bojiehttp://www.blogger.com/profile/13163354520521690612noreply@blogger.com3tag:blogger.com,1999:blog-8502142627599800928.post-37295756747475808532015-02-17T15:22:00.000-08:002015-02-17T15:22:00.570-08:00Tableau Visualization -- How to build Gnatt ChartGnatt chart can be useful in showing status of large number of items and things with respect to time. An example of that is displaying the various flight time from each route and determine which has the longest delay or longest flight time as reflected on the chart by the size of each Gnatt.<br />
<br />
In my example, i am going to show for a given year, how are the order to ship time is like for each product in the warehouse.<br />
<br />
So start with product dimension (Row section) and order date (Column section):<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmTzH3jk1I40GOmhM5rWAvydw4_xnT-QpdZVfslCBXoVc6t-FODoDm8tb1yNrEDi6wXBZ3FR4qCbkUowlDFprzzUrOrwz7SvGDx-zFs5kQnxtCB-E2QHT-c0_3odYb8d_GeRi3aqVWM7U/s1600/1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmTzH3jk1I40GOmhM5rWAvydw4_xnT-QpdZVfslCBXoVc6t-FODoDm8tb1yNrEDi6wXBZ3FR4qCbkUowlDFprzzUrOrwz7SvGDx-zFs5kQnxtCB-E2QHT-c0_3odYb8d_GeRi3aqVWM7U/s1600/1.jpg" height="360" width="640" /></a></div>
<br />
<br />
change the order date display format to 'exact date'<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgbx6YnZZ0dke6FhjY562W0yngYIajlvvyig4j50FpJQWFdXiD9LoOUVnAgbSBkoMo9Wduq0qecbfPZtiODNH-l6zZ3XWNVAsQ0ESUzFX0nTT0qTX3sgi5JzVgd15sgX5bYqvIq1LXkYS0/s1600/2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgbx6YnZZ0dke6FhjY562W0yngYIajlvvyig4j50FpJQWFdXiD9LoOUVnAgbSBkoMo9Wduq0qecbfPZtiODNH-l6zZ3XWNVAsQ0ESUzFX0nTT0qTX3sgi5JzVgd15sgX5bYqvIq1LXkYS0/s1600/2.jpg" height="360" width="640" /></a></div>
<br />
Now to figure out the exact order to ship time, we simply go a date diff between order date and ship date. This is similar to timestampdiff function in SQL, but in tableau we use a different syntax by create a new calculated field called 'Time to ship' with the following formula:<br />
<br />
DATEDIFF('day',[Order Date],[Ship Date] )<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEitToMr_E53HEdjoMRXV5qJHvR1jKnDqXr0krwnFScmQA14Km87jy9bxePTtpCC47bJa-NCXWVdH_1AxF31_9e_gKk3EyD-cWrDTVWNKW0Q2MoZLFygZNglL3hInZ8JPSZUC_uaz4QomDs/s1600/3.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEitToMr_E53HEdjoMRXV5qJHvR1jKnDqXr0krwnFScmQA14Km87jy9bxePTtpCC47bJa-NCXWVdH_1AxF31_9e_gKk3EyD-cWrDTVWNKW0Q2MoZLFygZNglL3hInZ8JPSZUC_uaz4QomDs/s1600/3.jpg" height="360" width="640" /></a></div>
<br />
Sometimes, there are different types of shipping. So if you want to make a distinction among the dimension with certain categorization, we simply put the categorizing field into the color window. So in this case, different product has different ship mode that we want to distinguish by, so simply drop ship mode into the color section as shown below and the chart is colored.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg57bL_sGF9HUJgEh3PQOkSJCWGSih4NdpZAJLAQgWqTlMcYTTXm1P4EdWWeyxC4ZCklYLlfAeXRebLBK_TKAM3PqwLPpTrFF3zMpHFzJTXcjTxLYR0dnGa-Mmbtf4CtlpW-01h4DTFeA8/s1600/4.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg57bL_sGF9HUJgEh3PQOkSJCWGSih4NdpZAJLAQgWqTlMcYTTXm1P4EdWWeyxC4ZCklYLlfAeXRebLBK_TKAM3PqwLPpTrFF3zMpHFzJTXcjTxLYR0dnGa-Mmbtf4CtlpW-01h4DTFeA8/s1600/4.jpg" height="360" width="640" /></a></div>
<br />
<br />
Now move 'time to ship' field into size windows. The size of each bar will start to differ more noticeably based on the number of days differ.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiB48KMwV05pCcnSs6DikAPXRHUuY0KyiFV8hmv3XcB8GpwCTOm_b9rUfXsPlKvf0qZzvU8R01tlmmeUgXt_PdvhAaC1CxsAwPsOZ2plXv2zYsWD9GNs1v8PwRtxTW7oB__csY9k6uQb8U/s1600/5.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiB48KMwV05pCcnSs6DikAPXRHUuY0KyiFV8hmv3XcB8GpwCTOm_b9rUfXsPlKvf0qZzvU8R01tlmmeUgXt_PdvhAaC1CxsAwPsOZ2plXv2zYsWD9GNs1v8PwRtxTW7oB__csY9k6uQb8U/s1600/5.jpg" height="360" width="640" /></a></div>
<br />
We also want to include the ship date as part of the display in details for validation purpose. So we pick ship date and drop it into detail windows<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjxC3FxY03AKO64IzObNGA6C5YccGmM_ITWNzSNQvH2AGz7O-nHPp4batCE1RxJtBjsj9Vj2ntM5QUyghwmw_Qgg5rBiQByIVyaw6iyFk8Niba3q3b2Wj-b1wKW045kjDHB-ZSeIdRI-0Y/s1600/6.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjxC3FxY03AKO64IzObNGA6C5YccGmM_ITWNzSNQvH2AGz7O-nHPp4batCE1RxJtBjsj9Vj2ntM5QUyghwmw_Qgg5rBiQByIVyaw6iyFk8Niba3q3b2Wj-b1wKW045kjDHB-ZSeIdRI-0Y/s1600/6.jpg" height="360" width="640" /></a></div>
<br />
Now the chart is done. We can move the mouse over any bars and the detail information will display from the details:<br />
<br />
here you can see the time to ship for applicance is 17 days and it shows you the date ordered and the date shipped<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhLNQTLqORt59J9hAwWpQdV9ilYKtz7K_wTxmFm380xQeoTmxQmhcCNJrgaqX1-i3DttKL6bG1PqXzDyzJLMrl4X5GvoVA-xLPPhtULafp-ue82_4h3MHYtbDA5E1s-f9N9mei3vIQ_rGU/s1600/8.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhLNQTLqORt59J9hAwWpQdV9ilYKtz7K_wTxmFm380xQeoTmxQmhcCNJrgaqX1-i3DttKL6bG1PqXzDyzJLMrl4X5GvoVA-xLPPhtULafp-ue82_4h3MHYtbDA5E1s-f9N9mei3vIQ_rGU/s1600/8.jpg" height="360" width="640" /></a></div>
<br />
<br />
Thanks<br />
<br />
Until next timeBojiehttp://www.blogger.com/profile/13163354520521690612noreply@blogger.com7tag:blogger.com,1999:blog-8502142627599800928.post-47234108671192591132015-02-12T17:58:00.000-08:002015-02-12T17:58:00.286-08:00Tableau Visualization -- How to build lollipop ChartLollipop chart is a fancy and cute chart that has become pretty popular. It is named such way because the shape of the graph consist of a bar attached to a circle, forming a shape of Lollipop. This chart can be useful to provide visualization for your measure numbers in terms of 'how long' it occupies the horizontal axis and the precious location of the measure numbers on the horizontal axis as a dot. The length of the bar is determined by the size of the measure number and the location of the dot on the axis is also determined by the same measure number. Lets take a look at this graph that I start with:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijHlzwYPBsNKtCwBEXrzMZd7ShWTLhNvD8QoFBCML_ZyxyNIadGRXVT7Sy3qm0LgfI4lom4qmy2bvi2hrIesjQ9BDyODMdgINmNbEZ8eQPaoAlQXZsgOW11b7VrNQdfBaxHeH9J2Vjp7I/s1600/1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijHlzwYPBsNKtCwBEXrzMZd7ShWTLhNvD8QoFBCML_ZyxyNIadGRXVT7Sy3qm0LgfI4lom4qmy2bvi2hrIesjQ9BDyODMdgINmNbEZ8eQPaoAlQXZsgOW11b7VrNQdfBaxHeH9J2Vjp7I/s1600/1.jpg" height="236" width="640" /></a></div>
<br />
Here, I have sales from the measure which I put on the column section along with Region as the dimension. In the row I have Product, another dimension.<br />
<br />
Now, right-click on either 'sales' field and check 'Dual-Axis':<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRY0SXatTPJP9vpjWe1KTUQyw1dcOR9k0Y98cotS83BUx1zk9x5HI2oml2WyK9E9uaQ3Nwx5ulpHj5Bquv5yufYlVIscyg1M-rlgSXtbSY0CC8UHwcOiHpaQ034nayNyvw6GxQVdKL014/s1600/2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRY0SXatTPJP9vpjWe1KTUQyw1dcOR9k0Y98cotS83BUx1zk9x5HI2oml2WyK9E9uaQ3Nwx5ulpHj5Bquv5yufYlVIscyg1M-rlgSXtbSY0CC8UHwcOiHpaQ034nayNyvw6GxQVdKL014/s1600/2.jpg" height="360" width="640" /></a></div>
<br />
Now chart transformed:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhGt_R_VZC7gUR-pMS6st7QTpUwp3MBoYvKxlgatcpEZaeL14sJuRp-K0f_3CQEgFSahIMRUx8b9D7LRkI8GD0xjKn6MnXDJYnGFAuXhvmTSgz3laHBGjlbiltJCB-81LXg7yq1i0XKm0I/s1600/3.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhGt_R_VZC7gUR-pMS6st7QTpUwp3MBoYvKxlgatcpEZaeL14sJuRp-K0f_3CQEgFSahIMRUx8b9D7LRkI8GD0xjKn6MnXDJYnGFAuXhvmTSgz3laHBGjlbiltJCB-81LXg7yq1i0XKm0I/s1600/3.jpg" height="360" width="640" /></a></div>
<br />
Notice on the left side where i highlighted, there are 2 'sales' under marks windows. Click on each of those 2 highlighted icons, it will open up marks options that you can set for the displays of each of 'sales' field.<br />
<br />
So first one is to set the first 'sales' as bar display:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgn2TZZAU7zX3dSMGlAF-Q2xjYa7BlJV0qWef2BPjVFlvX3LmtyyORJxlr-pJZ1mtRiffJ8F3YzryiMg0zLD64hhw_L4HeEOKbTZHmFSO5xnPGS48kExE03AiVVNUZsapyVrIL3JLxP-hM/s1600/4.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgn2TZZAU7zX3dSMGlAF-Q2xjYa7BlJV0qWef2BPjVFlvX3LmtyyORJxlr-pJZ1mtRiffJ8F3YzryiMg0zLD64hhw_L4HeEOKbTZHmFSO5xnPGS48kExE03AiVVNUZsapyVrIL3JLxP-hM/s1600/4.jpg" height="360" width="640" /></a></div>
<br />
And second 'sales' as circle display<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqepSwSQ7m9LuU5cvjy-8heXDcC0vRbc00zUf-fmMoGpgNkimw1BF01L7VsVq-l3Bd_xajQ2cT-JXe2EnilZ4CrGI6SHo-IilA_1e9Ixv9dU7ytn4oAmh_Od9fYVv7En7_NmZu4KMhaZ4/s1600/5.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqepSwSQ7m9LuU5cvjy-8heXDcC0vRbc00zUf-fmMoGpgNkimw1BF01L7VsVq-l3Bd_xajQ2cT-JXe2EnilZ4CrGI6SHo-IilA_1e9Ixv9dU7ytn4oAmh_Od9fYVv7En7_NmZu4KMhaZ4/s1600/5.jpg" height="360" width="640" /></a></div>
<br />
<br />
Adjust the size of each of the fields, it changes the graph into looking like a lollipop by making the size of the bar smaller and size of the circle bigger:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEigKq9tjaujvCbz49-rjLdrxhbMCD2zHasRVFK33fmUs6cCcGmgB0DSqgZI1kcLAb1ZjKfpWkkAMqGAzKwl_plHsBHdsZnQhFuWYQNXTUAfYoWu3j_-XwWv71bXYnxad-P5zhfVMCEZMd4/s1600/6.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEigKq9tjaujvCbz49-rjLdrxhbMCD2zHasRVFK33fmUs6cCcGmgB0DSqgZI1kcLAb1ZjKfpWkkAMqGAzKwl_plHsBHdsZnQhFuWYQNXTUAfYoWu3j_-XwWv71bXYnxad-P5zhfVMCEZMd4/s1600/6.jpg" height="360" width="640" /></a></div>
<br />
<br />
Now, let's create another field called profit ratio, which is calculated by taking Sales and dividing it by profit:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg2rRZl7GrKn4s1cOYGTXjjUs-9Q4P7N7PG6uwSrfzee7L9myxYDPeErWOgAo5ypqlwwBtxCUE__8z5lAS9JeeN5U_isuI8unYXtda2rN93tToaJidqP21PC7ljmg7sMafwz2CKjAAe-es/s1600/7.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg2rRZl7GrKn4s1cOYGTXjjUs-9Q4P7N7PG6uwSrfzee7L9myxYDPeErWOgAo5ypqlwwBtxCUE__8z5lAS9JeeN5U_isuI8unYXtda2rN93tToaJidqP21PC7ljmg7sMafwz2CKjAAe-es/s1600/7.jpg" height="360" width="640" /></a></div>
<br />
<br />
After that, drag and drop profit ratio into color windows:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirh43D_Snxb2h-l0t6SO9oCtOo2RlydfmttiRwgDQOAo5u0VS0wW6fXTdiFcni1VaMgf7bcvkF4Nxc2nASo6Cm62B5OpDXDhhZKJhK2sJ4pxc7XL7eZp9nzCPpbguW1fFaKZ60zOtMhx0/s1600/8.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirh43D_Snxb2h-l0t6SO9oCtOo2RlydfmttiRwgDQOAo5u0VS0wW6fXTdiFcni1VaMgf7bcvkF4Nxc2nASo6Cm62B5OpDXDhhZKJhK2sJ4pxc7XL7eZp9nzCPpbguW1fFaKZ60zOtMhx0/s1600/8.jpg" height="360" width="640" /></a></div>
<br />
Now we have create a Lollipop chart that gives you 2 different visualization of the sales and also displays the color divergence based on the profitability. If you move the mouse over to any point on the graph, it will display the detail numbers on the chart including regions, product, sales and profit ratios.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgRNXgRjEOGfcSrbZzrbGJSXTfQ9NyAzhxOD3FpsQfQ0yCBxbxdgzTcwkFMHbB10LdS8G7qCuQumptNRG_kys5s8nDm10dG4gcyMKg76Y6kBa1BXDtxpfUpXzvbF_zgyNrOYqzvNXnjPEg/s1600/9.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgRNXgRjEOGfcSrbZzrbGJSXTfQ9NyAzhxOD3FpsQfQ0yCBxbxdgzTcwkFMHbB10LdS8G7qCuQumptNRG_kys5s8nDm10dG4gcyMKg76Y6kBa1BXDtxpfUpXzvbF_zgyNrOYqzvNXnjPEg/s1600/9.jpg" height="360" width="640" /></a></div>
<br />
<br />
Thanks, until next timeBojiehttp://www.blogger.com/profile/13163354520521690612noreply@blogger.com9tag:blogger.com,1999:blog-8502142627599800928.post-4444956675821088032015-02-10T17:30:00.000-08:002015-02-10T17:30:00.704-08:00Tableau Visualization -- How to build Dot chartDot chart is a pretty typically statistical graph where each data point on the graph are marked in a circle shape. Making dot chart in Tableau is pretty simple and standard.<br />
<br />
Lets start with sample data set and we put 'state or province' as dimension then 'sales' and 'profit' as measures all into the row section of the report and set the display under 'Mark' as circle:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5OFSPa2aB4IZGQYKg5AyMdRkgv0MciG0Ud79H5JNahigfdexxJqRKjEEkfpzQRGfY-JkNeW-kuGHHDS8hfYD-vpSGWKgoUlL396mtTKrkcc_uaqP-sqibYEJfRBx6TghPrBKA3_bh5h4/s1600/1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5OFSPa2aB4IZGQYKg5AyMdRkgv0MciG0Ud79H5JNahigfdexxJqRKjEEkfpzQRGfY-JkNeW-kuGHHDS8hfYD-vpSGWKgoUlL396mtTKrkcc_uaqP-sqibYEJfRBx6TghPrBKA3_bh5h4/s1600/1.jpg" height="344" width="640" /></a></div>
<br />
Then, drag and drop both sales and profit from rows into the highlighted vertical axis on the graph:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgsP2ZfFs6ehtZmg_NOX00G0eyUhYOnPeeZZH2JAKOzjk3RV5drHCTQMR4FcOQCkcwsTkUegQQ9qgKzJHrJ5UYD04eKy01as5fDdJtUApLwFi15AshyNt4mLTaztLG3Ug_3z3SYo5yJxt0/s1600/2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgsP2ZfFs6ehtZmg_NOX00G0eyUhYOnPeeZZH2JAKOzjk3RV5drHCTQMR4FcOQCkcwsTkUegQQ9qgKzJHrJ5UYD04eKy01as5fDdJtUApLwFi15AshyNt4mLTaztLG3Ug_3z3SYo5yJxt0/s1600/2.jpg" height="344" width="640" /></a></div>
<br />
After this action, you will see that 'Measure name' and 'Measure value' appears on the row and column section automatically, we will put 'measure name' in to color window under 'Marks':<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgFPxHHbh5piDvTqfJITEeXF86sJvSXFPCiuzfC1GM7g4TTGQ_jPR9O2jLIaa5AG-O7uypWf7L73uOkqM8qtIrcNJunP0RTgklv0jVkhnh6Zf6STMepFQdNCUybulBgNsZCfzgoWpO-3nM/s1600/3.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgFPxHHbh5piDvTqfJITEeXF86sJvSXFPCiuzfC1GM7g4TTGQ_jPR9O2jLIaa5AG-O7uypWf7L73uOkqM8qtIrcNJunP0RTgklv0jVkhnh6Zf6STMepFQdNCUybulBgNsZCfzgoWpO-3nM/s1600/3.jpg" height="344" width="640" /></a></div>
<br />
Now the graph becomes like the following, which we will move the 'measure value' into column section:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTHcxbofplw5qFbQeQdtYenj1WLxpeFkeuZIGfa7WWgrydyuCyyzMFVpv6eBtkc1nfhyphenhyphenbmnIt4KXAIiHfVE34VrMmuE2QiMgPboU4t_SsGLcx13V47opmQjyPaQepIIMjo_Uj-fZ3DQXc/s1600/4.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTHcxbofplw5qFbQeQdtYenj1WLxpeFkeuZIGfa7WWgrydyuCyyzMFVpv6eBtkc1nfhyphenhyphenbmnIt4KXAIiHfVE34VrMmuE2QiMgPboU4t_SsGLcx13V47opmQjyPaQepIIMjo_Uj-fZ3DQXc/s1600/4.jpg" height="344" width="640" /></a></div>
<br />
<br />
<br />
Now we get the following view:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEisVNQpA8eB4cItwN8yJgJxvc_d5JgA7VMM7TAIFphlke_9hvkP_O8RVJvMrb-vgtUBUXucFdKywy-zeil9Cg-cOVYSH7AYJZng5hCQH4JZF2ToTA226NXzCKC-3ZyCO4iFYKas5wOsV08/s1600/5.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEisVNQpA8eB4cItwN8yJgJxvc_d5JgA7VMM7TAIFphlke_9hvkP_O8RVJvMrb-vgtUBUXucFdKywy-zeil9Cg-cOVYSH7AYJZng5hCQH4JZF2ToTA226NXzCKC-3ZyCO4iFYKas5wOsV08/s1600/5.jpg" height="344" width="640" /></a></div>
<br />
<br />
Pivot the graph and we get the final view:<br />
<br />
This view displays the sales and profit of each state on the graph separated by 2 dots in different colors<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKqFCJf1JOMO2IChiw7LicliFQtfYqqm5MFtRRApn0dKVSAO60U7P1h4GxdMBLAzvCtH7VAxP9hWtobIyPwogf5_xtWNk6hZRESahq_LeVJhcmvgwhsF6YQ2Dox5L31pJV89kyIJRi4Qw/s1600/6.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKqFCJf1JOMO2IChiw7LicliFQtfYqqm5MFtRRApn0dKVSAO60U7P1h4GxdMBLAzvCtH7VAxP9hWtobIyPwogf5_xtWNk6hZRESahq_LeVJhcmvgwhsF6YQ2Dox5L31pJV89kyIJRi4Qw/s1600/6.jpg" height="344" width="640" /></a></div>
<br />
<br />
Thanks<br />
<br />
Until next time<br />
<br />
<br />Bojiehttp://www.blogger.com/profile/13163354520521690612noreply@blogger.com0tag:blogger.com,1999:blog-8502142627599800928.post-35823177845509762982015-02-06T16:03:00.000-08:002015-02-06T16:03:00.506-08:00Tableau Visualization -- How to build Pareto chartPareto chart can be very good for highlighting certain items from a very large set of factors based on certain criteria, Tableau has a very powerful way of creating pareto chart.<br />
<br />
We will start with a simple dimension (product name ) in Column and measures (sales) in rows, in order for the chart to make sense, there should be a lot of products:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYUDua5bNYyjm38zMT8oaTgRgidhPOlDl02ngX_gkPa7I9qVbgkCjh4x9vOKSgJ-REEKCe5jOB8fL6rrsonyOGmSBcaAFHCrhJA6K4l_Ji1-bJq7PIWEc_fKJ_LPbeTbONg1khvO7a1eU/s1600/x2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYUDua5bNYyjm38zMT8oaTgRgidhPOlDl02ngX_gkPa7I9qVbgkCjh4x9vOKSgJ-REEKCe5jOB8fL6rrsonyOGmSBcaAFHCrhJA6K4l_Ji1-bJq7PIWEc_fKJ_LPbeTbONg1khvO7a1eU/s1600/x2.jpg" height="344" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<br />
Now in our particular example, we are comparing the running total and percent of total of the sales per product. So we create table calculation on sales. In this case, percent of total is the secondary type calculation after running total:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBtt8L1PKljsZDKqDfsQKJIZRWOJ6m5rX4oZ9Ow-Oo-w-sg_0Sc51rM_77J1fVJhYFcxLVkD5LI7NdKqaaCpIhGIS0LacesBNUnusiMV_QqSuv4v73xRRdwShZ6YNvVbVXW1PQ-aw81TU/s1600/x3.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBtt8L1PKljsZDKqDfsQKJIZRWOJ6m5rX4oZ9Ow-Oo-w-sg_0Sc51rM_77J1fVJhYFcxLVkD5LI7NdKqaaCpIhGIS0LacesBNUnusiMV_QqSuv4v73xRRdwShZ6YNvVbVXW1PQ-aw81TU/s1600/x3.jpg" height="360" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<br />
The graph changes afterwards to the following:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjg66K9ZATPuzN0ITyq9_2GQ64jWD-j7v3KRp3FEPZ48o4KQsbJegFPa_-HmHl7F1oU_BNadruigdGGBtV114PwHpQbbIMIRGZSiO93tXbm23iBMAP0dnxOb4WqjCvuiE8C6pPcAke8RJY/s1600/x4.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjg66K9ZATPuzN0ITyq9_2GQ64jWD-j7v3KRp3FEPZ48o4KQsbJegFPa_-HmHl7F1oU_BNadruigdGGBtV114PwHpQbbIMIRGZSiO93tXbm23iBMAP0dnxOb4WqjCvuiE8C6pPcAke8RJY/s1600/x4.jpg" height="360" width="640" /></a></div>
<br />
<br />
Now in this example, we are trying to compare the running sum of sales and total sum of sales, if when Total Sum/running is greater than 0.8, we should recognize those product item as above 80% of all. So in this case we will create a new calculated item called In Top 80% with the following formula: RUNNING_SUM(SUM([Sales])) / TOTAL(SUM([Sales]))<= 0.8<br />
<br />
Unlike writing in SQL where we may have to put it in a case statement, here in Tableau since the condition is expecting a binary output: yes or no, and nothing else, the above formula is a simpler way to do so. It will divide the entire set with either > 0.8 or <=0.8<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgexsleHSHmx7ymH6Rdkzi2p2AsA9k2GsJYmf5ZHBnaaZc213gQx6Q7XAVYYDU1wJUWkNl2XYGEuVsouLEPSZUW2w_eCE-TSGXSyLVFeYNBtEa5Z1U130NFiYq3vD4BZsQ61jue_V6OFpw/s1600/4.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgexsleHSHmx7ymH6Rdkzi2p2AsA9k2GsJYmf5ZHBnaaZc213gQx6Q7XAVYYDU1wJUWkNl2XYGEuVsouLEPSZUW2w_eCE-TSGXSyLVFeYNBtEa5Z1U130NFiYq3vD4BZsQ61jue_V6OFpw/s1600/4.jpg" height="344" width="640" /></a></div>
<br />
<br />
Now move it over to color box under marks, this will automatically divide the entire product list into 2 colored groups based on whether the divided value is greater than or less than 0.8<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiUWzaIEZFIq8vKkrwDYpQIyCAnrx6BMv9flJ5beqRTKjKX-cUuGOTfvHRtJM6V60X4K1Fnx3d5yCqjl9JRxd-uwBTf-N1v9w-kf2kt98vdNWPsR9ERMP7UglM4pSgBFz3OtpGZdR91b88/s1600/x5.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiUWzaIEZFIq8vKkrwDYpQIyCAnrx6BMv9flJ5beqRTKjKX-cUuGOTfvHRtJM6V60X4K1Fnx3d5yCqjl9JRxd-uwBTf-N1v9w-kf2kt98vdNWPsR9ERMP7UglM4pSgBFz3OtpGZdR91b88/s1600/x5.jpg" height="272" width="640" /></a></div>
<br />
<br />
like so:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEimdrMCW_wviAVvmTNjomhWRTUb2yn734L4KKOEEDVUCOP7bY3RprcgEnRww4DbAmDRykGF6_LX0gFPZONu_pLCOGJsa1hpEfGTrXwwjrWoYlcJeJY4dmm2q5x7k-eI6yJt-_0DXK7qngI/s1600/x6.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEimdrMCW_wviAVvmTNjomhWRTUb2yn734L4KKOEEDVUCOP7bY3RprcgEnRww4DbAmDRykGF6_LX0gFPZONu_pLCOGJsa1hpEfGTrXwwjrWoYlcJeJY4dmm2q5x7k-eI6yJt-_0DXK7qngI/s1600/x6.jpg" height="344" width="640" /></a></div>
<br />
Now change the fitting from normal to entire view in order to see the big picture:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2mATtSuJKQ62jhyFfHvY6DkySE2jE7vkaB4iq8mKmJGdGmvyI0YsMEf_gVriEkBrIi7cOB20cxDlthbLfSsTACDGesSSo8-H64Wvu_XY3wrNsiKq-a5-Kk0Zqp2MEEXSK_zoT2YaDLMU/s1600/x7.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2mATtSuJKQ62jhyFfHvY6DkySE2jE7vkaB4iq8mKmJGdGmvyI0YsMEf_gVriEkBrIi7cOB20cxDlthbLfSsTACDGesSSo8-H64Wvu_XY3wrNsiKq-a5-Kk0Zqp2MEEXSK_zoT2YaDLMU/s1600/x7.jpg" height="344" width="640" /></a></div>
<br />
Entire picture:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghLsk1xEVp5CFI3VvnZkcfHFgVgsi2duynjkx32QEbjQvpk03ykGd2-kr5oNp4l6thZxsU66jv9djaPGAAHyTickU1YBr8iOG6EWzqSD9cEixPJm9w5YJbeIxT1-Lw8Gl0vpiEOV4uRe8/s1600/x8.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghLsk1xEVp5CFI3VvnZkcfHFgVgsi2duynjkx32QEbjQvpk03ykGd2-kr5oNp4l6thZxsU66jv9djaPGAAHyTickU1YBr8iOG6EWzqSD9cEixPJm9w5YJbeIxT1-Lw8Gl0vpiEOV4uRe8/s1600/x8.jpg" height="344" width="640" /></a></div>
<br />
Now lets add a reference line in the graph by right clicking on the vertical Axis, and we set the line value at 0.8 and make it constant.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjVopkfMzRdYgJZEQWn-ON1cxyCh-mc8NRvHM0JgCuxkvuQn1uG0OI5CH8cUvxcw5UN4nmTKnnzB9CjUW_zVMPNK2TvX8u7QTu_wJlZRHBOf39ffi6jdQPaCQennfJm62sMZ88XeTp3GH8/s1600/x9.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjVopkfMzRdYgJZEQWn-ON1cxyCh-mc8NRvHM0JgCuxkvuQn1uG0OI5CH8cUvxcw5UN4nmTKnnzB9CjUW_zVMPNK2TvX8u7QTu_wJlZRHBOf39ffi6jdQPaCQennfJm62sMZ88XeTp3GH8/s1600/x9.jpg" height="360" width="640" /></a></div>
<br />
<br />
This will drop a line at the graph at around 80% area on the graph and it will stay constant:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijuvyFNeqvk8E1fV5z0YStuk3_w6cbE0KuS5mnRScu_NxQ46zzuA8bXv_ebL_BfZJpgiJJZtoeu0QhJOuL_PGavExIM5PJAiMwzZy2bt5dU_LA_MjPivzHHrvI3GhkvuVm77cjU7UrEJg/s1600/x10.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijuvyFNeqvk8E1fV5z0YStuk3_w6cbE0KuS5mnRScu_NxQ46zzuA8bXv_ebL_BfZJpgiJJZtoeu0QhJOuL_PGavExIM5PJAiMwzZy2bt5dU_LA_MjPivzHHrvI3GhkvuVm77cjU7UrEJg/s1600/x10.jpg" height="360" width="640" /></a></div>
<br />
Now, let's sort the graph by product in ascending order by sales:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjqFDbEUWYP4x1GFqrcgg17PejBCqax7yBXLlnWPMvRyqlrSpJIgh5G_pIp2Q-xnkXzkIeN5TKv-CMWrAx5bvBejwLPxVYu20v4Tg8nE9nHdJQaguG_L8jek2JIzQw7nFerml2ZAMfe4uY/s1600/x11.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjqFDbEUWYP4x1GFqrcgg17PejBCqax7yBXLlnWPMvRyqlrSpJIgh5G_pIp2Q-xnkXzkIeN5TKv-CMWrAx5bvBejwLPxVYu20v4Tg8nE9nHdJQaguG_L8jek2JIzQw7nFerml2ZAMfe4uY/s1600/x11.jpg" height="360" width="640" /></a></div>
<br />
<br />
<br />
Now the Pareto chart is done:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjqFDbEUWYP4x1GFqrcgg17PejBCqax7yBXLlnWPMvRyqlrSpJIgh5G_pIp2Q-xnkXzkIeN5TKv-CMWrAx5bvBejwLPxVYu20v4Tg8nE9nHdJQaguG_L8jek2JIzQw7nFerml2ZAMfe4uY/s1600/x11.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjqFDbEUWYP4x1GFqrcgg17PejBCqax7yBXLlnWPMvRyqlrSpJIgh5G_pIp2Q-xnkXzkIeN5TKv-CMWrAx5bvBejwLPxVYu20v4Tg8nE9nHdJQaguG_L8jek2JIzQw7nFerml2ZAMfe4uY/s1600/x11.jpg" height="360" width="640" /></a></div>
<br />
<br />
<br />
Thanks<br />
<br />
Until next timeBojiehttp://www.blogger.com/profile/13163354520521690612noreply@blogger.com1tag:blogger.com,1999:blog-8502142627599800928.post-7627459183253423782015-02-01T15:56:00.000-08:002015-02-01T15:56:00.035-08:00Tableau Visualization -- How to build thermometer chartThermometer is a cool visual that allows audience to visually look at different matrix that the difference can be visually indicated by the different color, size in one chart as if it was like Thermometer.I experimented a few ways to build a basic thermometer chart using tableau, this is a pretty cool visual.<br />
<br />
1. Lets start with 2 dimensions and 2 matrix. Here I am using customer segment and region as dimension and sales and profit as matrix by putting the dimensions in column section and matrix in row section. The report will automatically look like the following:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg0wu9DLy2wh5DFGiCWkuEay9aoxVjGDHRN9cTN-cOHip_FaUd80C3xVw_Q3IVxtOJsC7N_FgrTL83r3rYN_M8A1cNAZX7-lii_Ntdk0bfzyPNWeRvR_YfGW0XzwVKgKLDyNpW3vpEec8U/s1600/1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg0wu9DLy2wh5DFGiCWkuEay9aoxVjGDHRN9cTN-cOHip_FaUd80C3xVw_Q3IVxtOJsC7N_FgrTL83r3rYN_M8A1cNAZX7-lii_Ntdk0bfzyPNWeRvR_YfGW0XzwVKgKLDyNpW3vpEec8U/s1600/1.jpg" height="302" width="640" /></a></div>
<br />
<br />
2. Drag and drop both matrix columns into the Y (Vertical axis) of the chart:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZGzJ0lfqUBk4k_a3lGhSRr79h5Ea4LXHw6bCoVw1OLAIxbaRXQB_bf7vMomskqQOGx6wVZR-DnGlUPVMlB7JYuzGndnB8YADXovmLbxfeT_dsPTfaNkphlhXen5NFadaRcgoYoairKQI/s1600/2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZGzJ0lfqUBk4k_a3lGhSRr79h5Ea4LXHw6bCoVw1OLAIxbaRXQB_bf7vMomskqQOGx6wVZR-DnGlUPVMlB7JYuzGndnB8YADXovmLbxfeT_dsPTfaNkphlhXen5NFadaRcgoYoairKQI/s1600/2.jpg" height="344" width="640" /></a></div>
<br />
The chart will automatically transform to the following view after dropping both matrix. You will notice that 'Measure Names' and 'Measure Values' automatically appear in the following highlighted area<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBc0nPMGSKCn1oTCFR40JYCoMSF8xoDBWqyCiunM81CTxdwn13aWIfgA9UfxsowDwNAsV-4BQ9-4IIuWhf73csKGDfIOk9gNYXJAJf6SaP7KdGU9D_YuS6EIa2CzEJq8JO1_Kveyj2KTs/s1600/3.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBc0nPMGSKCn1oTCFR40JYCoMSF8xoDBWqyCiunM81CTxdwn13aWIfgA9UfxsowDwNAsV-4BQ9-4IIuWhf73csKGDfIOk9gNYXJAJf6SaP7KdGU9D_YuS6EIa2CzEJq8JO1_Kveyj2KTs/s1600/3.jpg" height="334" width="640" /></a></div>
<br />
3. Move 'Measure Names' to Color windows under 'Marks':<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijAfWtElguhzDltBNJo87tDAIzClYtQvHhzTDsAHbE_MQIeyTRzTzT1KMZxx_GZcz6yjv0v_NGEfOqhrvnV8S2z7wcQiQWdYDGVPk5gdDfmYvP6LZ5YyVgM88z5Ez3pRcmRMMNMhny5mg/s1600/4.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijAfWtElguhzDltBNJo87tDAIzClYtQvHhzTDsAHbE_MQIeyTRzTzT1KMZxx_GZcz6yjv0v_NGEfOqhrvnV8S2z7wcQiQWdYDGVPk5gdDfmYvP6LZ5YyVgM88z5Ez3pRcmRMMNMhny5mg/s1600/4.jpg" height="344" width="640" /></a></div>
<br />
The chart will look at the following after done:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5juM-nW7rVImjj4E1PJYfj9ftwDNQ6VLdXLSV6zW_BfuleP2-ihtxeHhh6tnHKimEI0AjCGGXowfLBTnuMQqDqGXRJy62ifsXgXvPtmX3FI9WLRo4NCT6xx2c3ILHqzhWv2ECSto7hPc/s1600/5.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5juM-nW7rVImjj4E1PJYfj9ftwDNQ6VLdXLSV6zW_BfuleP2-ihtxeHhh6tnHKimEI0AjCGGXowfLBTnuMQqDqGXRJy62ifsXgXvPtmX3FI9WLRo4NCT6xx2c3ILHqzhWv2ECSto7hPc/s1600/5.jpg" height="306" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
This time drop measure name under size windows:</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjOj81ruiNy3CfCcx6lcrHQsTw7g0RERHUgGP3Y4o3LaygX-3kcmpvSks_L7bdiigjpgyJTn0Az14TTxUeo994ExrjntBWqb0byIrmEAzE220oEdecc4BjeH0fAVRwEFVUFr6sqSg-TJpA/s1600/6.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjOj81ruiNy3CfCcx6lcrHQsTw7g0RERHUgGP3Y4o3LaygX-3kcmpvSks_L7bdiigjpgyJTn0Az14TTxUeo994ExrjntBWqb0byIrmEAzE220oEdecc4BjeH0fAVRwEFVUFr6sqSg-TJpA/s1600/6.jpg" height="344" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
And you get the following chart that the 2 bars stack on each other:</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg0C9KEwp_yqyw0Mzzh3r179QzRaVuhdKoggBnEtgo6o4DNlRVtQTXqnA4A7jvx9BUsaRROvT12EoboYz7XoVf11qtHccz4rXtxXGV_IaXpdBFRBONMt9fZqer7A0IQeNH3u8Wol6sIf50/s1600/7.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg0C9KEwp_yqyw0Mzzh3r179QzRaVuhdKoggBnEtgo6o4DNlRVtQTXqnA4A7jvx9BUsaRROvT12EoboYz7XoVf11qtHccz4rXtxXGV_IaXpdBFRBONMt9fZqer7A0IQeNH3u8Wol6sIf50/s1600/7.jpg" height="292" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
4. Now unstack the bars by going to analysis--> stack marks --> off<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiVpc4if5MdWAvgYxOfnEhCas5SAuivISrVSvxtv3ZflxP8mS_iQ0bmbFVnNqZJ7LWeLgI-ZpY_lhyPhcvMSeU7em702lOMSVMjI-vD22cgHTxZ259hQqOJknA0c0LRt7uyjXGIgdGyeZg/s1600/8.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiVpc4if5MdWAvgYxOfnEhCas5SAuivISrVSvxtv3ZflxP8mS_iQ0bmbFVnNqZJ7LWeLgI-ZpY_lhyPhcvMSeU7em702lOMSVMjI-vD22cgHTxZ259hQqOJknA0c0LRt7uyjXGIgdGyeZg/s1600/8.jpg" height="360" width="640" /></a></div>
<br />
<br />
Now you get your thermo chart:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8-ETT0JKxnj9krWFnnmSExJKVRGAPpe_1ThS16Fv6wExf-ti2L3ZPGsxHt14SVg_bsp_fC1POxMgzVt2s28LoTmO20oGCoLpQnKSBDBJi4kGAlD8DranSH867FlHvoC9kd2MGuQiBkgs/s1600/9.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8-ETT0JKxnj9krWFnnmSExJKVRGAPpe_1ThS16Fv6wExf-ti2L3ZPGsxHt14SVg_bsp_fC1POxMgzVt2s28LoTmO20oGCoLpQnKSBDBJi4kGAlD8DranSH867FlHvoC9kd2MGuQiBkgs/s1600/9.jpg" height="302" width="640" /></a></div>
<br />
Thanks<br />
<br />
Until next timeBojiehttp://www.blogger.com/profile/13163354520521690612noreply@blogger.com1tag:blogger.com,1999:blog-8502142627599800928.post-40721695292223292792015-01-26T17:59:00.000-08:002015-01-26T17:59:00.098-08:00Tableau visualization - How to create sparkline chart<br />
Having had some time working with Tableau, i have found some of the charting features that Tableau provides is very impressive and easy to use once you get used to it. Sparkline chart is one of those charts. Here is how to create your basic sparkline chart for beginners of this tool.<br />
<br />
We will start with sample data set (superstore subset) that comes with Tableau installation:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHwnDxcF7zxGdbJPHZE6Y9SfNpq2mWLJ9IOmIE0ZmKjnkECgmfGmcfLcuDanTVYMZ4V240rlX5dZyZWqH5Kfa080yWiF3M3s4py29TuBhunKJW-Lasb5pDid9viCKh_Dp9fpg7q-Roo5c/s1600/sample+data1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHwnDxcF7zxGdbJPHZE6Y9SfNpq2mWLJ9IOmIE0ZmKjnkECgmfGmcfLcuDanTVYMZ4V240rlX5dZyZWqH5Kfa080yWiF3M3s4py29TuBhunKJW-Lasb5pDid9viCKh_Dp9fpg7q-Roo5c/s1600/sample+data1.jpg" height="266" width="640" /></a></div>
<br />
<br />
In the worksheet, we will bring 1 dimension and 2 measures to the 'Row' section. The report will look like the following as soon as I am done with dragging region and sales (drag twice) over:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhCxQ7k7-8BxKp3CcYovKnBzztmywRo7I5D0DIoFdsHweLZvfTG7jB76VC7gDqrKkloBkvPIygHK5MCefD86lut9vgBLUEA1N8pUwdbbvFZL-WjDspxJ4VD5bU9H7p7Lc7Gn8eXVdFDMx0/s1600/1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhCxQ7k7-8BxKp3CcYovKnBzztmywRo7I5D0DIoFdsHweLZvfTG7jB76VC7gDqrKkloBkvPIygHK5MCefD86lut9vgBLUEA1N8pUwdbbvFZL-WjDspxJ4VD5bU9H7p7Lc7Gn8eXVdFDMx0/s1600/1.jpg" height="344" width="640" /></a></div>
<br />
<br />
Then bring the date over as well by dragging Order Date over to the column section. By default, it automatically adds year function to order date and the graph shows 2 identical lines per region because of the 2 sales measures<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvYr5InqcUk6LYUJZvKADUQhaGQaNzhO6Mfi7TOWoF-b529BfxWLRppGbkTMeJmjNw_ybTwI4gQeqTHak_k53cP34CWiip1VsgvJBnWdOV8bQ3xyIYHsq88WaPxmoGiee6cypngU5QyLE/s1600/2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvYr5InqcUk6LYUJZvKADUQhaGQaNzhO6Mfi7TOWoF-b529BfxWLRppGbkTMeJmjNw_ybTwI4gQeqTHak_k53cP34CWiip1VsgvJBnWdOV8bQ3xyIYHsq88WaPxmoGiee6cypngU5QyLE/s1600/2.jpg" height="328" width="640" /></a></div>
<br />
<br />
Then click on one of the measure and do 'Add table calculation'. Here we are setting it as 'Moving Calculation' in the calculation type and choose Average for aggregation. I selected previous 5 months and check the box 'Include current value'. This is to calculate the moving average of sales for the previous 6 months including current month.<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjMMlM8WWbztqZ9PyW7ioUAOjADwGiEbPdTMsOajdM9RGQXC81fpkdqMhPq6nXtDdNMLkFm1SvVge86DSCqIaPMIvXMTdWax1k-CYk4Wvw3q9XI3VIAkBHKhjPHOnQahkORml7_zjCl4_c/s1600/3.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjMMlM8WWbztqZ9PyW7ioUAOjADwGiEbPdTMsOajdM9RGQXC81fpkdqMhPq6nXtDdNMLkFm1SvVge86DSCqIaPMIvXMTdWax1k-CYk4Wvw3q9XI3VIAkBHKhjPHOnQahkORml7_zjCl4_c/s1600/3.jpg" height="344" width="640" /></a></div>
<br />
<br />
Then drag each of the measures and drop then under the axis section of the graph as you can see it gets highlighted when you move the cursor over.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjy-ZENvk1O74XbpAxZVmnbHL8rhET-_kC9VROWow3hHn_DbX5lVNTF1rZIOA5Qe4SdrUeRu717pYjs9pKCEUwUYMpCOg9ubb7p6Y6VfHEod08296a0CmCShBhSBi1D2IO6JJykO6q1nDY/s1600/4.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjy-ZENvk1O74XbpAxZVmnbHL8rhET-_kC9VROWow3hHn_DbX5lVNTF1rZIOA5Qe4SdrUeRu717pYjs9pKCEUwUYMpCOg9ubb7p6Y6VfHEod08296a0CmCShBhSBi1D2IO6JJykO6q1nDY/s1600/4.jpg" height="344" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
After that the graph automatically becomes the following. You can change the formatting, hide the axis labels if you wish or change the colors of the 2 lines as you wish. In my case, i changed the Date function from year to Month, because that's my intended moving calculation.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
This graph will provide user with visuals that displays the sales per region per month as well as it's moving 6 month average on the same chart as sparklines. </div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbPzO7iCK1mW8lV97_wkDBL9o8c_9OaK0iTohbW-lC6PQNqsrvbamIVZpc4anWjUJF-4zlDabUz95jeQfNpZqawjlcwxE6bW4A2quTUS2_BgotElHOzLZbu-DvZDgajvTbKNZfnB6KLLk/s1600/5.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbPzO7iCK1mW8lV97_wkDBL9o8c_9OaK0iTohbW-lC6PQNqsrvbamIVZpc4anWjUJF-4zlDabUz95jeQfNpZqawjlcwxE6bW4A2quTUS2_BgotElHOzLZbu-DvZDgajvTbKNZfnB6KLLk/s1600/5.jpg" height="344" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Thanks </div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Until next time..</div>
<br />Bojiehttp://www.blogger.com/profile/13163354520521690612noreply@blogger.com0tag:blogger.com,1999:blog-8502142627599800928.post-79803994086434132252014-11-22T11:43:00.002-08:002014-11-22T11:43:54.418-08:00OBIEE -- Writeback update while keep the update history <br />
Hello<br />
<br />
I implemented the writeback in OBIEE as mentioned in this <a href="http://obinsight.blogspot.com/2014/06/how-to-update-report-and-automatically.html" target="_blank">post</a>, however the writeback feature in OBIEE is very basic, it does simple overriding of the data and it doesn't keep the historic changes. I have tried playing around with the writeback.xml file with insert or update tags, but i had no luck with it. Therefore, I decided a different approach.<br />
<br />
The original table that I was using for writeback and reporting will be kept as it is. Now I am going to create another table with the same table structure. Then I will create a trigger that will insert records to this table when the original table gets a writeback update. Then last but not least, I will create a view which unions these 2 tables and use it in OBIEE rpd. Let me illustrate here:<br />
<br />
The original table being used in the older <a href="http://obinsight.blogspot.com/2014/06/how-to-update-report-and-automatically.html" target="_blank">post</a> is FA_Table, which is being used in rpd.<br />
<br />
Create a new table called FA_Table_AUX:<br />
<br />
CREATE TABLE FA_TABLE_AUX<br />
(<br />
LEDGER_NAME VARCHAR2(30), <br />
PERIOD_NAME VARCHAR2(15), <br />
JE_HEADER_NAME VARCHAR2(100), <br />
JE_LINE_NUM NUMBER, <br />
.<br />
OTHER COLUMNS<br />
.<br />
.<br />
)<br />
<br />
Then create the trigger called FA_WRITEBACK:<br />
<br />
CREATE OR REPLACE TRIGGER FA_WRITEBACK<br />
BEFORE UPDATE<br />
ON FA_TABLE<br />
FOR EACH ROW<br />
<br />
BEGIN<br />
INSERT<br />
INTO FA_TABLE_AUX<br />
(<br />
LEDGER_NAME,<br />
PERIOD_NAME,<br />
JE_HEADER_NAME,<br />
JE_LINE_NUM,<br />
CURRENCY_CODE,<br />
CURRENCY_CONVERSION_RATE,<br />
CURRENCY_CONVERSION_TYPE,<br />
CURRENCY_CONVERSION_DATE,<br />
POSTED_DATE,<br />
JE_BATCH_ID,<br />
JE_CATEGORY,<br />
JE_SOURCE,<br />
ENTERED_DR,<br />
ENTERED_CR,<br />
ACCOUNTED_DR,<br />
ACCOUNTED_CR,<br />
EXCHANGE_RATE_USD,<br />
TRANSACTION_LOCAL_AMOUNT,<br />
TRANSACTION_USD_AMOUNT,<br />
JOURNAL_LINE_DESCRIPTION,<br />
EFFECTIVE_DATE,<br />
COMPANY,<br />
COST_CENTER,<br />
COST_CENTER_NAME,<br />
ACCOUNT,<br />
ACCOUNT_NAME,<br />
PRODUCT,<br />
PROJECT,<br />
PROJECT_NAME,<br />
INTERCOMPANY,<br />
GEOGRAPHY,<br />
GEOGRAPHY_NAME,<br />
FUTURE2,<br />
INVOICE_NUM,<br />
INVOICE_AMOUNT,<br />
INVOICE_LINE_DESCRIPTION,<br />
VENDOR_NAME,<br />
INVOICE_DATE,<br />
INVOICE_GL_DATE,<br />
INVOICE_PERIOD_NAME,<br />
DIST_GL_PERIOD_NAME,<br />
PO_NUM,<br />
RECORD_TYPE,<br />
PERIOD_NUM,<br />
ASSET_NUMBER,<br />
GLOBAL_TRANSFER,<br />
REGION,<br />
INTERIM_CATEGORY,<br />
CAPEX_CATEGORY,<br />
CAPEX_YES_NO,<br />
RECORD_ID,<br />
REVISION_NUMBER,<br />
CURRENT_REVISION,<br />
LAST_UPDATED_BY,<br />
LAST_UPDATED_DATE,<br />
RAW_DATA_FLAG,<br />
FINAL_DATA_FLAG<br />
)<br />
VALUES<br />
(<br />
:old.LEDGER_NAME,<br />
:old.PERIOD_NAME,<br />
:old.JE_HEADER_NAME,<br />
:old.JE_LINE_NUM,<br />
:old.CURRENCY_CODE,<br />
:old.CURRENCY_CONVERSION_RATE,<br />
:old.CURRENCY_CONVERSION_TYPE,<br />
:old.CURRENCY_CONVERSION_DATE,<br />
:old.POSTED_DATE,<br />
:old.JE_BATCH_ID,<br />
:old.JE_CATEGORY,<br />
:old.JE_SOURCE,<br />
:old.ENTERED_DR,<br />
:old.ENTERED_CR,<br />
:old.ACCOUNTED_DR,<br />
:old.ACCOUNTED_CR,<br />
:old.EXCHANGE_RATE_USD,<br />
:old.TRANSACTION_LOCAL_AMOUNT,<br />
:old.TRANSACTION_USD_AMOUNT,<br />
:old.JOURNAL_LINE_DESCRIPTION,<br />
:old.EFFECTIVE_DATE,<br />
:old.COMPANY,<br />
:old.COST_CENTER,<br />
:old.COST_CENTER_NAME,<br />
:old.ACCOUNT,<br />
:old.ACCOUNT_NAME,<br />
:old.PRODUCT,<br />
:old.PROJECT,<br />
:old.PROJECT_NAME,<br />
:old.INTERCOMPANY,<br />
:old.GEOGRAPHY,<br />
:old.GEOGRAPHY_NAME,<br />
:old.FUTURE2,<br />
:old.INVOICE_NUM,<br />
:old.INVOICE_AMOUNT,<br />
:old.INVOICE_LINE_DESCRIPTION,<br />
:old.VENDOR_NAME,<br />
:old.INVOICE_DATE,<br />
:old.INVOICE_GL_DATE,<br />
:old.INVOICE_PERIOD_NAME,<br />
:old.DIST_GL_PERIOD_NAME,<br />
:old.PO_NUM,<br />
:old.RECORD_TYPE,<br />
:old.PERIOD_NUM,<br />
:old.ASSET_NUMBER,<br />
:old.GLOBAL_TRANSFER,<br />
:old.REGION,<br />
:old.INTERIM_CATEGORY,<br />
:old.CAPEX_CATEGORY,<br />
:old.CAPEX_YES_NO,<br />
:old.RECORD_ID,<br />
:old.REVISION_NUMBER,<br />
'N',<br />
'Trigger',<br />
SYSDATE,<br />
:old.RAW_DATA_FLAG,<br />
'N');<br />
<br />
:new.REVISION_NUMBER := :old.REVISION_NUMBER+1;<br />
END;<br />
<br />
--------------------------<br />
<br />
Here as you can see, the old records from FA_Table gets inserted into FA_Table_AUX, and the last 5 fields will be hard-coded. It also updates the revision_number by 1 increment just to indicate the update versions.<br />
<br />
Now create a view that union both of these tables. The <b>BOLD </b>columns will be included in the report and it will reflect the historical changes because the columns in FB_TABLE_AUX will be inserted based on trigger.<br />
<br />
create or replace view FA_TABLE_AUD_TRAIL<br />
(LEDGER_NAME,<br />
PERIOD_NAME,<br />
JE_HEADER_NAME,<br />
JE_LINE_NUM,<br />
CURRENCY_CODE,<br />
CURRENCY_CONVERSION_RATE,<br />
CURRENCY_CONVERSION_TYPE,<br />
CURRENCY_CONVERSION_DATE,<br />
POSTED_DATE,<br />
JE_BATCH_ID,<br />
JE_CATEGORY,<br />
JE_SOURCE,<br />
ENTERED_DR,<br />
ENTERED_CR,<br />
ACCOUNTED_DR,<br />
ACCOUNTED_CR,<br />
EXCHANGE_RATE_USD,<br />
TRANSACTION_LOCAL_AMOUNT,<br />
TRANSACTION_USD_AMOUNT,<br />
JOURNAL_LINE_DESCRIPTION,<br />
EFFECTIVE_DATE,<br />
COMPANY,<br />
COST_CENTER,<br />
COST_CENTER_NAME,<br />
ACCOUNT,<br />
ACCOUNT_NAME,<br />
PRODUCT,<br />
PROJECT,<br />
PROJECT_NAME,<br />
INTERCOMPANY,<br />
GEOGRAPHY,<br />
GEOGRAPHY_NAME,<br />
FUTURE2,<br />
INVOICE_NUM,<br />
INVOICE_AMOUNT,<br />
INVOICE_LINE_DESCRIPTION,<br />
VENDOR_NAME,<br />
INVOICE_DATE,<br />
INVOICE_GL_DATE,<br />
INVOICE_PERIOD_NAME,<br />
DIST_GL_PERIOD_NAME,<br />
PO_NUM,<br />
RECORD_TYPE,<br />
PERIOD_NUM,<br />
ASSET_NUMBER,<br />
GLOBAL_TRANSFER,<br />
REGION,<br />
INTERIM_CATEGORY,<br />
CAPEX_CATEGORY,<br />
CAPEX_YES_NO,<br />
RECORD_ID,<br />
<b>REVISION_NUMBER,</b><br />
<b> CURRENT_REVISION,</b><br />
<b> LAST_UPDATED_BY,</b><br />
<b> LAST_UPDATED_DATE,</b><br />
<b> RAW_DATA_FLAG,</b><br />
<b> FINAL_DATA_FLAG)</b><br />
as<br />
select<br />
LEDGER_NAME,<br />
PERIOD_NAME,<br />
JE_HEADER_NAME,<br />
JE_LINE_NUM,<br />
CURRENCY_CODE,<br />
CURRENCY_CONVERSION_RATE,<br />
CURRENCY_CONVERSION_TYPE,<br />
CURRENCY_CONVERSION_DATE,<br />
POSTED_DATE,<br />
JE_BATCH_ID,<br />
JE_CATEGORY,<br />
JE_SOURCE,<br />
ENTERED_DR,<br />
ENTERED_CR,<br />
ACCOUNTED_DR,<br />
ACCOUNTED_CR,<br />
EXCHANGE_RATE_USD,<br />
TRANSACTION_LOCAL_AMOUNT,<br />
TRANSACTION_USD_AMOUNT,<br />
JOURNAL_LINE_DESCRIPTION,<br />
EFFECTIVE_DATE,<br />
COMPANY,<br />
COST_CENTER,<br />
COST_CENTER_NAME,<br />
ACCOUNT,<br />
ACCOUNT_NAME,<br />
PRODUCT,<br />
PROJECT,<br />
PROJECT_NAME,<br />
INTERCOMPANY,<br />
GEOGRAPHY,<br />
GEOGRAPHY_NAME,<br />
FUTURE2,<br />
INVOICE_NUM,<br />
INVOICE_AMOUNT,<br />
INVOICE_LINE_DESCRIPTION,<br />
VENDOR_NAME,<br />
INVOICE_DATE,<br />
INVOICE_GL_DATE,<br />
INVOICE_PERIOD_NAME,<br />
DIST_GL_PERIOD_NAME,<br />
PO_NUM,<br />
RECORD_TYPE,<br />
PERIOD_NUM,<br />
ASSET_NUMBER,<br />
GLOBAL_TRANSFER,<br />
REGION,<br />
INTERIM_CATEGORY,<br />
CAPEX_CATEGORY,<br />
CAPEX_YES_NO,<br />
RECORD_ID,<br />
REVISION_NUMBER,<br />
CURRENT_REVISION,<br />
LAST_UPDATED_BY,<br />
LAST_UPDATED_DATE,<br />
RAW_DATA_FLAG,<br />
FINAL_DATA_FLAG<br />
from FA_TABLE<br />
union<br />
select<br />
LEDGER_NAME,<br />
PERIOD_NAME,<br />
JE_HEADER_NAME,<br />
JE_LINE_NUM,<br />
CURRENCY_CODE,<br />
CURRENCY_CONVERSION_RATE,<br />
CURRENCY_CONVERSION_TYPE,<br />
CURRENCY_CONVERSION_DATE,<br />
POSTED_DATE,<br />
JE_BATCH_ID,<br />
JE_CATEGORY,<br />
JE_SOURCE,<br />
ENTERED_DR,<br />
ENTERED_CR,<br />
ACCOUNTED_DR,<br />
ACCOUNTED_CR,<br />
EXCHANGE_RATE_USD,<br />
TRANSACTION_LOCAL_AMOUNT,<br />
TRANSACTION_USD_AMOUNT,<br />
JOURNAL_LINE_DESCRIPTION,<br />
EFFECTIVE_DATE,<br />
COMPANY,<br />
COST_CENTER,<br />
COST_CENTER_NAME,<br />
ACCOUNT,<br />
ACCOUNT_NAME,<br />
PRODUCT,<br />
PROJECT,<br />
PROJECT_NAME,<br />
INTERCOMPANY,<br />
GEOGRAPHY,<br />
GEOGRAPHY_NAME,<br />
FUTURE2,<br />
INVOICE_NUM,<br />
INVOICE_AMOUNT,<br />
INVOICE_LINE_DESCRIPTION,<br />
VENDOR_NAME,<br />
INVOICE_DATE,<br />
INVOICE_GL_DATE,<br />
INVOICE_PERIOD_NAME,<br />
DIST_GL_PERIOD_NAME,<br />
PO_NUM,<br />
RECORD_TYPE,<br />
PERIOD_NUM,<br />
ASSET_NUMBER,<br />
GLOBAL_TRANSFER,<br />
REGION,<br />
INTERIM_CATEGORY,<br />
CAPEX_CATEGORY,<br />
CAPEX_YES_NO,<br />
RECORD_ID,<br />
REVISION_NUMBER,<br />
CURRENT_REVISION,<br />
LAST_UPDATED_BY,<br />
LAST_UPDATED_DATE,<br />
RAW_DATA_FLAG,<br />
FINAL_DATA_FLAG<br />
from<br />
FA_TABLE_AUX<br />
<br />
Then in the rpd file, simply replace the physical layer table with FA_TABLE_AUD_TRAIL. No need to change the writeback.xml, because the writeback.xml is still using update FA_TABLE as implemented from the <a href="http://obinsight.blogspot.com/2014/06/how-to-update-report-and-automatically.html" target="_blank">older post</a>, When it is trying to update FA_TABLE with user inputs, the trigger will be invoked and therefore the same row before user updates will be inserted into FA_TABLE_AUX table while FA_TABLE will have the updated data.. Since we are reporting against the view which unions both table, then from the OBIEE dashboard, you will get to see both new and older records.<br />
<br />
If you are in doubt, you can always query against FB_TABLE_AUX from the DB, if it is blank, that means no insertion occurs, if it returns 1 row, that means user has trigger the update..<br />
<br />
Thank you<br />
<br />
Until next time..Bojiehttp://www.blogger.com/profile/13163354520521690612noreply@blogger.com2tag:blogger.com,1999:blog-8502142627599800928.post-57756233861072866902014-08-19T16:39:00.000-07:002014-08-19T16:39:01.525-07:00OBIEE: Dynamically calculate quarterly difference without using time series functionSometimes, you have a custom report built from custom schema, it could be a materialized view or view that were custom made, where the number is at a certain granularity without hierarchy defined. Like you have a quarterly report, but now the requirement is to display the numbers not only in the quarter that user pick, but also display the previous quarter of that user defined quarter along. Not only that, you also need to display the difference between the quarters as well as the monthly measures under these 2 quarters. Like the report below for instance:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6mILSClQoxy7kToJByJewI814IuIYEK9GZQLRng4HCzESoVhNalY-CTEZzKhIp-UJxaXHx5NCqNMZ9UQtpzenGomymSbXu2iqIRhwMCaYIT64k_6Pd43Fe3MTElVyPlAN0zunPS5mp64/s1600/deprn1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6mILSClQoxy7kToJByJewI814IuIYEK9GZQLRng4HCzESoVhNalY-CTEZzKhIp-UJxaXHx5NCqNMZ9UQtpzenGomymSbXu2iqIRhwMCaYIT64k_6Pd43Fe3MTElVyPlAN0zunPS5mp64/s1600/deprn1.jpg" height="284" width="640" /></a></div>
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
As you can see from the report, the user just pick 2014 Q3 and the report comes back with Q3 as well as Q2 data with month and quarter column pivoted. Now the pivot view allows us to get the total per quarter, which as you can see, is done. Now the challenge is, how are we going calculate the difference between these 2 quarterly difference?<br />
<br />
<br />
So, let me go back a little bit. Before I got to the report was you can see, I created a new column in the RPD which brings Year and Quarter concatenated based on what this table have. In other words, i take '2014' and '3', then concatenated it 'Q' in the middle to get you 2014 Q3. 'Trim' function is used here to get rid of any unwanted spaces that is trailing as a result of converting number into string.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgxLLvFPXvstnsr2sVbIhabOXNQPoBi4nkFjLugf7qYj8BfgyElGu9o96G47kVbZ_k4XtFPwbitnVVwqmeYrsfR5h8JBkJAKzbKfR9iEWWOP9OqqHNPwtO0UOpvhGBbKumT7rrWtNZYKmo/s1600/deprn2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgxLLvFPXvstnsr2sVbIhabOXNQPoBi4nkFjLugf7qYj8BfgyElGu9o96G47kVbZ_k4XtFPwbitnVVwqmeYrsfR5h8JBkJAKzbKfR9iEWWOP9OqqHNPwtO0UOpvhGBbKumT7rrWtNZYKmo/s1600/deprn2.jpg" height="486" width="640" /></a></div>
<br />
The next step I decided to do is to create a simple report which takes the user input quarter value, then it should return that quarter value as well as the previous quarter value using the rpd column that I just created above.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBBX8B0c40_l8Um_2_pY46S4ZyTRBiF_3opdNc2ouo7ydVSuYiEwiy0sZGROOSX7ET-2w3XVUIJgrWu18B412Or_4AJf5vTHEOTHxVg_hdAjVyrTqU-1yKZi5Hs_5RLSG7JW96XsSaxXE/s1600/deprn3.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBBX8B0c40_l8Um_2_pY46S4ZyTRBiF_3opdNc2ouo7ydVSuYiEwiy0sZGROOSX7ET-2w3XVUIJgrWu18B412Or_4AJf5vTHEOTHxVg_hdAjVyrTqU-1yKZi5Hs_5RLSG7JW96XsSaxXE/s1600/deprn3.jpg" height="262" width="640" /></a></div>
<br />
In the above report, 'Current Year Quarter' is nothing but the RPD column, the Previous Year QTR has the following formula that returns the previous Quarter:<br />
<br />
Since the time dimension does have Date (DATE_VALUE) column, by doing timestampadd at month level with '-3' will automatically give you the month that is 3 month prior to the month of that corresponding DATE_VALUE. It also needs to address the fact that the prior quarter of Q1 should be Q4 of the previous year, therefore a case statement is embedded to take care of this situation:<br />
<br />
TRIM(TRAILING ' ' FROM CAST(<b>case when "- XXFB_TIME_DIMENSION"."QUARTER" = 1 then "- XXFB_TIME_DIMENSION"."YEAR" - 1 else "- XXFB_TIME_DIMENSION"."YEAR" end</b> AS CHAR))||' '||'Q'||Trim(trailing ' ' from cast(QUARTER_OF_YEAR(<b>timestampadd(sql_tsi_month,-3,"- XXFB_TIME_DIMENSION"."DATE_VALUE</b>")) as char))<br />
<br />
After concatenating these components together, it finally gives me what I want:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj15_8KWOc6uH_TfCMyLu65_WolgdJvVYh22ZlPzMQxjGEhoB-zl6j1btOWe5J0xLjVyNeiRYsbpQunZsTpHSdMG5xZ64Lzm8JQcATiV0x59t7iYDGY0e3QwpbHhFs4maF6iQ_9lW1IHpA/s1600/deprn6.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj15_8KWOc6uH_TfCMyLu65_WolgdJvVYh22ZlPzMQxjGEhoB-zl6j1btOWe5J0xLjVyNeiRYsbpQunZsTpHSdMG5xZ64Lzm8JQcATiV0x59t7iYDGY0e3QwpbHhFs4maF6iQ_9lW1IHpA/s1600/deprn6.jpg" height="400" width="281" /></a></div>
<br />
This report will be accepting the presentation variable from the dashboard prompt quarter, so i call it 'Quarter'. Save this report as 'Previous Quarter'.<br />
<br />
Now, go back to the main report that I have been creating as you can see below:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJAQeN2GiOVmhpukMe5q1LR8n-VBBjv9zBdES-wEi5IvLSAWYJYAFlj_jKpHz17FOrdVwVzXS1mXy6xOqDi0eprtoG3ErimzxpeR6AGDx2sPz_f31fjpiMHryV9a6PT_Z2ifdoIjKpnDY/s1600/deprn4.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJAQeN2GiOVmhpukMe5q1LR8n-VBBjv9zBdES-wEi5IvLSAWYJYAFlj_jKpHz17FOrdVwVzXS1mXy6xOqDi0eprtoG3ErimzxpeR6AGDx2sPz_f31fjpiMHryV9a6PT_Z2ifdoIjKpnDY/s1600/deprn4.jpg" height="186" width="640" /></a></div>
<br />
<br />
Here in the filter section, the Year&Quarter column (which is the new RPD column) is getting value from 'Current Year Quarter' OR from 'Previous Year Column' of the report 'Previous Quarter' created above. This means that when user select 2014 Q3 from the dashboard, it will return 2014 Q3 and 2014 Q2, both will be passed to year&quarter column of this report through this filter condition. Then by pivoting the quarter and month column, it will display the view that you saw.<br />
<br />
Now the second thing is to come up with a way to calculate difference of these 2 quarters. Unfortunately, this will have to be done with a different calculation because time series function will make it very complicated and difficult to validate the logic.<br />
<br />
So first thing is to find a way to calculate the measure for each of the quarter and it's previous quarter. This can be done using 'filter' function by making the Year&Quarter column equal 'current quarter' or 'previous quarter' from report 'Previous quarter'.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhKblaahUwrYDkSEgifl9j7K1uLHP89ITT7ihI60H9OnvpMww1OOBQUg-jJQ6wTGnjPKTOBA2E-3gAoj20Ff-OfelvzQIYiJ9ciAGDwpkNzYhjR7yzZglCja4WfSD7r4noS22E7t3qH83I/s1600/deprn5.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhKblaahUwrYDkSEgifl9j7K1uLHP89ITT7ihI60H9OnvpMww1OOBQUg-jJQ6wTGnjPKTOBA2E-3gAoj20Ff-OfelvzQIYiJ9ciAGDwpkNzYhjR7yzZglCja4WfSD7r4noS22E7t3qH83I/s1600/deprn5.jpg" height="434" width="640" /></a></div>
<br />
As it turns out, OBIEE does allow to use filter function with nested query, so this is what the expression looks like below:<br />
<br />
FILTER(ifnull(FACT."Accounted Net USD",0) + ifnull(FACT."Accounted Net USD",0) USING ("- XXFB_TIME_DIMENSION"."Year&Quarter" IN (SELECT saw_0 FROM (SELECT "- XXFB_TIME_DIMENSION"."Year&Quarter" saw_0, TRIM(TRAILING ' ' FROM CAST(case when "- XXFB_TIME_DIMENSION"."QUARTER" = 1 then "- XXFB_TIME_DIMENSION"."YEAR" - 1 else "- XXFB_TIME_DIMENSION"."YEAR" end AS CHAR))||' '||'Q'||Trim(trailing ' ' from cast(QUARTER_OF_YEAR(timestampadd(sql_tsi_month,-3,"- XXFB_TIME_DIMENSION"."DATE_VALUE")) as char)) saw_1 FROM "FB - Custom Views for Oracle Financials" WHERE "- XXFB_TIME_DIMENSION"."Year&Quarter" IN ('@{Quarter}')) nqw_1 ))) - FILTER(ifnull(FACT."Accounted Net USD",0) + ifnull(FACT."Accounted Net USD",0) USING ("- XXFB_TIME_DIMENSION"."Year&Quarter" IN (SELECT saw_1 FROM (SELECT "- XXFB_TIME_DIMENSION"."Year&Quarter" saw_0, TRIM(TRAILING ' ' FROM CAST(case when "- XXFB_TIME_DIMENSION"."QUARTER" = 1 then "- XXFB_TIME_DIMENSION"."YEAR" - 1 else "- XXFB_TIME_DIMENSION"."YEAR" end AS CHAR))||' '||'Q'||Trim(trailing ' ' from cast(QUARTER_OF_YEAR(timestampadd(sql_tsi_month,-3,"- XXFB_TIME_DIMENSION"."DATE_VALUE")) as char)) saw_1 FROM "FB - Custom Views for Oracle Financials" WHERE "- XXFB_TIME_DIMENSION"."Year&Quarter" IN ('@{Quarter}')) nqw_1 )))<br />
<br />
Now the break down of this expression is below:<br />
<br />
<b><span style="font-family: Trebuchet MS, sans-serif;">(Below will get you Current month measure)</span></b><br />
FILTER(<span style="color: red;">ifnull(FACT."Accounted Net USD",0) + ifnull(FACT."Accounted Net USD",0</span>) USING<br />
("- XXFB_TIME_DIMENSION"."Year&Quarter" IN<br />
<span style="font-family: Trebuchet MS, sans-serif;"><b>----- (<span style="color: red;">The red part</span> is the measure that we are dealing with)</b></span><br />
<br />
<br />
(SELECT <span style="color: blue;"><b>saw_0</b></span> FROM (SELECT "- XXFB_TIME_DIMENSION"."Year&Quarter" saw_0, TRIM(TRAILING ' ' FROM CAST(case when "- XXFB_TIME_DIMENSION"."QUARTER" = 1 then "- XXFB_TIME_DIMENSION"."YEAR" - 1 else "- XXFB_TIME_DIMENSION"."YEAR" end AS CHAR))||' '||'Q'||Trim(trailing ' ' from cast(QUARTER_OF_YEAR(timestampadd(sql_tsi_month,-3,"- XXFB_TIME_DIMENSION"."DATE_VALUE")) as char)) saw_1 FROM "FB - Custom Views for Oracle Financials" WHERE "- XXFB_TIME_DIMENSION"."Year&Quarter" IN <b><u>('@{Quarter}')</u></b>) nqw_1 ))) <br />
<span style="font-family: Trebuchet MS, sans-serif;"><b>---- this is the other report that we created earlier, presentation variable needs to be entered here, <span style="color: blue;">saw_0 </span>represent 'current year and quarter' from that report</b></span><br />
<br />
- <br />
<b><span style="font-family: Trebuchet MS, sans-serif;">(Below will get you previous month measure)</span></b><br />
FILTER(ifnull(FACT."Accounted Net USD",0) + ifnull(FACT."Accounted Net USD",0)<br />
<br />
USING ("- XXFB_TIME_DIMENSION"."Year&Quarter" IN<br />
<br />
(SELECT <span style="color: purple;"><b>saw_1</b></span> FROM (SELECT "- XXFB_TIME_DIMENSION"."Year&Quarter" saw_0, TRIM(TRAILING ' ' FROM CAST(case when "- XXFB_TIME_DIMENSION"."QUARTER" = 1 then "- XXFB_TIME_DIMENSION"."YEAR" - 1 else "- XXFB_TIME_DIMENSION"."YEAR" end AS CHAR))||' '||'Q'||Trim(trailing ' ' from cast(QUARTER_OF_YEAR(timestampadd(sql_tsi_month,-3,"- XXFB_TIME_DIMENSION"."DATE_VALUE")) as char)) saw_1 FROM "FB - Custom Views for Oracle Financials" WHERE "- XXFB_TIME_DIMENSION"."Year&Quarter" IN <u><b>('@{Quarter}')</b></u>) nqw_1 )))<br />
<b><span style="font-family: Trebuchet MS, sans-serif;">--- <span style="color: purple;">saw_1 </span>represents the 'previous year and quarter' from that report </span></b><br />
<b><span style="font-family: Trebuchet MS, sans-serif;"><br /></span></b>
<span style="font-family: inherit;">Now, test the new report:</span><br />
<span style="font-family: inherit;"><br /></span>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJG1iLymH0MgL-ixZXSJZ4a48M-oDQylFJVoOt_lpbwlKPIHfBjujKpndoRMJqat3WTWk-uvTHYC-cyZbJj9r5B-fT4OlR54I-fNGDYVBq-bGktARCARPv6BgHw6bGfvQjsVUUtqiffU4/s1600/deprn7.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJG1iLymH0MgL-ixZXSJZ4a48M-oDQylFJVoOt_lpbwlKPIHfBjujKpndoRMJqat3WTWk-uvTHYC-cyZbJj9r5B-fT4OlR54I-fNGDYVBq-bGktARCARPv6BgHw6bGfvQjsVUUtqiffU4/s1600/deprn7.jpg" height="278" width="640" /></a></div>
<span style="font-family: inherit;"><br /></span>
<span style="font-family: inherit;"><br /></span>
It's working nicely..<br />
<br />
ThanksBojiehttp://www.blogger.com/profile/13163354520521690612noreply@blogger.com1tag:blogger.com,1999:blog-8502142627599800928.post-43279598580913646932014-08-01T19:57:00.000-07:002014-08-01T19:57:00.401-07:00OBIEE vs Tableau ReportHaving used both technologies quite extensively, I can't help but to make some comparisons between the 2 tools.<br />
<br />
For those that are not quite familiar with Tableau, it is a pretty cool technology that also provides very good look and feels of the reports. It is a pretty popular technology in a lot of companies.<br />
<br />
Here are some of the cool things that tableau has to offer:<br />
1.<br />
Tableau allows developers to bypass the metadata design and go directly into the report building phases. In other words, there is no equivalence of Admin Tool in Tableau. You develop your SQL or PL/SQL, you then create reports and views based on this SQL. This allows the development to be very flexible because there is no needs for dimensional modeling. You can join as many tables, create as many self-joins, making as many snowflake or normalizations of dimensions as you want, as long as your query is giving you the right data, you can put it in tableau and it becomes the report you want. The same thing in OBIEE would probably have to be done through custom views or materialized views, which will still need to be migrated across the 3 layers in Admin tool.<br />
<br />
2.<br />
Tableau makes mapview much easier to build. All you need is spatial data in your table: latitude and Longitude and city. Once these fields are in your query, it will show up in tableau with different Icons letting you know they are available. You can then putting in the mapview and the map will be generated with every city in your table on the map. This process is much simpler than doing the same in OBIEE.<br />
<br />
3.<br />
The latest versions of Tableau provides your calender for any date filters (I am surprised it took this long for such important feature to come up ). The date filter has some pretty cool features that it allows users to not only filter based on dates, but also provides other filtering options such as 'last 2 weeks', 'last 2 months', 'between each Monday' with all just a click away. This can also be done in OBIEE but will require the use of variables, which will table some engineering.<br />
<br />
4.<br />
Tableau report allows users to pick their own filters while viewing the report. In OBIEE, the user will normally have to contact the report developers if they want to change filters and prompts.<br />
<br />
5. The common features that OBIEE has, such as agents and navigation among reports and views can also be found in tableau.<br />
<br />
6 Tableau is less complicated in its server architecture compared with OBIEE and weblogic.<br />
<br />
On the other hand, there are some downside of tableau as well compared with OBIEE:<br />
<br />
1.One of the main thing about Tableau is that it doesn't seem to have a query engine. This means it will try to query the entire DB first before applying any filters. This will cause a lot of overheads and oftenly at a enterprise level of reporting, the Tableau server is less stable than OBIEE because of the size of the Data they are dealing with.<br />
<br />
2.For enterprise reporting, there might be reports that has to go through auditing to meet certain compliance based on the formatting and other things. OBIEE reporting has been tested for this purpose while Tableau will still have to be trialed and tested. What's the use of having all of the fancy reports that don't pass the auditing?<br />
<br />
3.Another disadvantage of Tableau, which can be major for some companies, is that it doesn't allow writebacks. We all know that OBIEE has a writeback feature, which allows report users to update records on the dashboard without having to go to DB. This can be important in a lot of the financial reporting that the users typically would want to update certain records during their month-end closing. This feature is absent in Tableau.<br />
<br />
Overall, Tableau is more of a point solution rather than enterprise-wise solution like OBIEE. But Tableau is way less costly than OBIEE and much more flexible in it's design, which also means that the level of SQL Programming for Tableau is much higher than in OBIEE.<br />
<br />
I hope this helps<br />
<br />
Bojiehttp://www.blogger.com/profile/13163354520521690612noreply@blogger.com7tag:blogger.com,1999:blog-8502142627599800928.post-87334755471065663902014-07-25T19:28:00.002-07:002014-07-25T19:28:35.740-07:00How to report on 2 datasets across different database in OBIEEHello<br />
<br />
Today I want to talk about this feature in OBIEE that allows you to do cross-db reporting, which I think is pretty cool.<br />
<br />
Say you have 2 different result sets, they could be 2 tables or could be 2 queries. Now I want to see an OBIEE report that joins these 2 result sets based on some common dimensions, so how do we do it?<br />
<br />
First of all, these 2 objects will be residing on different physical folders with different connection pools, this makes it complicated if you want to do physical joins. <br />
<br />
In my example below, I have created 2 oblique views that contents 2 SQL queries, but they are located in 2 different physical folders:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhh4GxO1zzC0tPjRTruepXl87GJAGvoELluUuhPZYaWzH9y5O62UmWvQa4J5y7ewYSeSA7SEgaPC2_MC976Iq80YXr6nkOzSSvMF2UfCto9Mf43MC0vKM1RCBCohiWrQG0Hp-aFv1Fb2w0/s1600/cross1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhh4GxO1zzC0tPjRTruepXl87GJAGvoELluUuhPZYaWzH9y5O62UmWvQa4J5y7ewYSeSA7SEgaPC2_MC976Iq80YXr6nkOzSSvMF2UfCto9Mf43MC0vKM1RCBCohiWrQG0Hp-aFv1Fb2w0/s1600/cross1.jpg" height="384" width="640" /></a></div>
<br />
Table1 is GL_Balance, which is from 1 DB and table 2 is A_GL_Balance, which is from another DB..<br />
<br />
Both tables are actually SQL queries which already contents dimensions and measures from multiple DB tables.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipttNYiFsB_DD8P2yq6MbhMyi7JvKIXzQl2sUcYM6agwQAX04dJPrFcryFMQC_5-zBAWOu5GadEaOCCyLlM1W1GXX5vpS1sTnLxAVP95LwV4KLOgCYkueNq9Nv4e1YTMvZa1tjy0NcwZI/s1600/cross2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipttNYiFsB_DD8P2yq6MbhMyi7JvKIXzQl2sUcYM6agwQAX04dJPrFcryFMQC_5-zBAWOu5GadEaOCCyLlM1W1GXX5vpS1sTnLxAVP95LwV4KLOgCYkueNq9Nv4e1YTMvZa1tjy0NcwZI/s1600/cross2.jpg" height="316" width="640" /></a></div>
<br />
Now in order to create a report that queries both table across the DB, I will leave these 2 tables as it is in the physical layer, but in the logic layer, I will create a logical confirm dimension that separates the dimensional attributes from the measures. So in the BMM layer, it will become 3 tables, F1, F2 and D. F1 will only have begin_bal and close_bal from Table1 and F2 will have only Balance_Acct_Amt and Balance_Loc_Amt from Table2. Then D will have all of the attributes mapped to both physical tables, you can both this in any BMM folder.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjqGpRJEt1doCUrdycF5uz2zfhxCWu87h9nnL5YMFvkR1EoB89u06gVOroRLCh-e9xqlbqiCWuans2ClD6X5K0fI5Zqww0ER2OBOrmMnD3mluJRU9A2Cnh1G1yVTFqjFhTmTza3SP7ifCM/s1600/cross3.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjqGpRJEt1doCUrdycF5uz2zfhxCWu87h9nnL5YMFvkR1EoB89u06gVOroRLCh-e9xqlbqiCWuans2ClD6X5K0fI5Zqww0ER2OBOrmMnD3mluJRU9A2Cnh1G1yVTFqjFhTmTza3SP7ifCM/s1600/cross3.jpg" height="268" width="640" /></a></div>
<br />
<br />
<br />
<br />
Now in the dimension table, I will map the logical columns to both physical table as sources, which means this table will have 2 LTRs and each columns will have 2 sources mapped to:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_1rCeOdrFFf1__Yhxasx_5FhKScPN59QSPS4GORkbkyaoJL72BD50j6EOwQKavj1Y4TwD71IRT0M9Hwj0fmyvfyVvqnAY8rAYO4K00r1gtJIAdvFWH_o58-ARS_4JS3geeQX_4WfQNfU/s1600/cross4.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_1rCeOdrFFf1__Yhxasx_5FhKScPN59QSPS4GORkbkyaoJL72BD50j6EOwQKavj1Y4TwD71IRT0M9Hwj0fmyvfyVvqnAY8rAYO4K00r1gtJIAdvFWH_o58-ARS_4JS3geeQX_4WfQNfU/s1600/cross4.jpg" height="364" width="640" /></a></div>
<br />
You can see that this table has 2 LTRs and each column (company for instances) is mapped to 2 sources..<br />
<br />
The rest are pretty standard process, I will create a logical hierarchical dimension out of this table, set the hierarchy level at each fact and move everything into the presentation layer.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjUW5vyGyv4s93GRXa49v5s5YCSThVDrTHv4wRY5uUi0KpeL33J3zpGyMaT8EcHj32C-ySL3eNzeFTrPt0mXNoTarATn0nRx2BSSFhmxaH2g07wEyrynggNhltGlGS1SzCGb8V7YPwoVVQ/s1600/cross5.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjUW5vyGyv4s93GRXa49v5s5YCSThVDrTHv4wRY5uUi0KpeL33J3zpGyMaT8EcHj32C-ySL3eNzeFTrPt0mXNoTarATn0nRx2BSSFhmxaH2g07wEyrynggNhltGlGS1SzCGb8V7YPwoVVQ/s1600/cross5.jpg" height="316" width="640" /></a></div>
<br />
The reason I am putting all of the attributes in this table is because I want OBIEE to join these 2 datasets based on all of them, i think they are all common to one another. You can identify which columns are common between the 2 in your case and choose the right list.<br />
<br />
What OBIEE does at run time is that, it creates 2 queries that execute in each DB to get the results seperately, it then merge them together in one report.<br />
<br />
So I am creating a simple report by picking company and 1 measure from each table<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7r39yvp7FpBbVqH4ikp0V-_BtKExfgnZ3hFdUpBRJdq7BVu79AzZHzrbnEljiCepvUgVhEWZNC-HDri0D3yuY0GwQhhmXDpyF8d_C8lnA59clOgIO5cCTMkMz9DbnwU-QzcrAz7xFKj8/s1600/cross6.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7r39yvp7FpBbVqH4ikp0V-_BtKExfgnZ3hFdUpBRJdq7BVu79AzZHzrbnEljiCepvUgVhEWZNC-HDri0D3yuY0GwQhhmXDpyF8d_C8lnA59clOgIO5cCTMkMz9DbnwU-QzcrAz7xFKj8/s1600/cross6.jpg" height="166" width="640" /></a></div>
<br />
The resulting data:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEguDHqqz_H-5KiphF5JevGG9UNyUOovt6a7DyOE50ErVcgq6lGTW-41AELpwb26YvERg0pMNg6j-4HKweWE30-z_kd_PYKATrnCAdswl6HbxSzAOv03RkGeMkN5h1gPAS0mz4RTK1ur6KI/s1600/cross7.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEguDHqqz_H-5KiphF5JevGG9UNyUOovt6a7DyOE50ErVcgq6lGTW-41AELpwb26YvERg0pMNg6j-4HKweWE30-z_kd_PYKATrnCAdswl6HbxSzAOv03RkGeMkN5h1gPAS0mz4RTK1ur6KI/s1600/cross7.jpg" height="362" width="640" /></a></div>
<br />
<br />
Now the query log shows the following:<br />
<br />
The blue parts are just the SQL that each table was generated by.<br />
<br />
]]<br />
[2014-07-25T19:19:03.000-07:00] [OracleBIServerComponent] [TRACE:2] [USER-18] [] [ecid: 1710f6cbdf3264b0:6b7606d0:1476ec8e12a:-8000-00000000000061e2,0:1:9:5] [tid: eb631940] [requestid: 54700010] [sessionid: 54700000] [username: ] -------------------- Sending query to database named Omega Custom (id: <<1390911>>), connection pool named Omega Custom CP, logical request hash , physical request hash 8d6cb66e: [[</1390911><br />
<br />
select sum(T2060476.BEGIN_BAL) as c1,<br />
T2060476.SEGMENT1 as c2<br />
from<br />
(<span style="color: blue;">select </span><br />
<span style="color: blue;">gcc.concatenated_segments, </span><br />
<span style="color: blue;">gcc.segment1,</span><br />
<span style="color: blue;">gcc.segment2,</span><br />
<span style="color: blue;">gcc.segment3,</span><br />
<span style="color: blue;">gcc.segment4,</span><br />
<span style="color: blue;">gcc.segment5,</span><br />
<span style="color: blue;">gcc.segment6,</span><br />
<span style="color: blue;">gcc.segment7,</span><br />
<span style="color: blue;">gcc.segment8,</span><br />
<span style="color: blue;">gcc.enabled_flag ,</span><br />
<span style="color: blue;">gcc.end_date_active, </span><br />
<span style="color: blue;">gb.period_name,</span><br />
<span style="color: blue;">gb.currency_code,</span><br />
<span style="color: blue;">gb.translated_flag,</span><br />
<span style="color: blue;">fv.flex_value_set_id</span><br />
<span style="color: blue;">--,gcc.segment3||'-'||gcc.segment1||'-'||gcc.segment6 combo,</span><br />
<span style="color: blue;">,(NVL(gb.begin_balance_dr, 0) - NVL(gb.begin_balance_cr, 0)) begin_bal</span><br />
<span style="color: blue;">, (NVL(gb.period_net_dr, 0) - NVL(gb.period_net_cr, 0)) period_net</span><br />
<span style="color: blue;">, ((NVL(gb.begin_balance_dr, 0) - NVL(gb.begin_balance_cr, 0)) + (NVL(gb.period_net_dr, 0) - NVL(gb.period_net_cr, 0))) close_bal</span><br />
<span style="color: blue;">/*, (NVL(gb.begin_balance_dr, 0) - NVL(gb.begin_balance_cr, 0))) begin_bal</span><br />
<span style="color: blue;">, SUM((NVL(gb.period_net_dr, 0) - NVL(gb.period_net_cr, 0))) period_net</span><br />
<span style="color: blue;">, sum(((NVL(gb.begin_balance_dr, 0) - NVL(gb.begin_balance_cr, 0)) + (NVL(gb.period_net_dr, 0) - NVL(gb.period_net_cr, 0)))) close_bal */</span><br />
<span style="color: blue;">from </span><br />
<span style="color: blue;">gl_balances gb, </span><br />
<span style="color: blue;">gl_code_combinations gcc, </span><br />
<span style="color: blue;">fnd_flex_values fv</span><br />
<span style="color: blue;">where gcc.code_combination_id = gb.code_combination_id</span><br />
<span style="color: blue;">and gb.actual_flag='A'</span><br />
<span style="color: blue;">and gb.currency_code !='STAT'</span><br />
<span style="color: blue;">and gcc.segment2 = fv.flex_value</span><br />
<span style="color: blue;">--and gcc.segment3 like '213530'</span><br />
<span style="color: blue;">--and gb.translated_flag IS NULL</span><br />
<span style="color: blue;">--and gcc.segment1 like '304'</span><br />
<span style="color: blue;">--group by gcc.segment3||'-'||gcc.segment1||'-'||gcc.segment6</span><br />
<span style="color: blue;">order by 4</span><br />
) T2060476<br />
group by T2060476.SEGMENT1<br />
order by c2<br />
<br />
]]<br />
[2014-07-25T19:19:03.000-07:00] [OracleBIServerComponent] [TRACE:2] [USER-18] [] [ecid: 1710f6cbdf3264b0:6b7606d0:1476ec8e12a:-8000-00000000000061e2,0:1:9:5] [tid: eb631940] [requestid: 54700010] [sessionid: 54700000] [username: ] -------------------- Sending query to database named Oracle Data Warehouse (id: <<1390933>>), connection pool named Oracle Data Warehouse Connection Pool, logical request hash 5f0261e4, physical request hash 597bac0c: [[</1390933><br />
<br />
select sum(T2060495.BALANCE_ACCT_AMT) as c1,<br />
T2060495.COMPANY as c2<br />
from<br />
(<span style="color: blue;">Select</span><br />
<span style="color: blue;">C.GL_ACCOUNT_NAME,</span><br />
<span style="color: blue;">C.ACCOUNT_SEG1_CODE COMPANY,</span><br />
<span style="color: blue;">C.ACCOUNT_SEG2_CODE COST_CENTER,</span><br />
<span style="color: blue;">C.ACCOUNT_SEG3_CODE ACCOUNT,</span><br />
<span style="color: blue;">C.ACCOUNT_SEG4_CODE PRODUCT,</span><br />
<span style="color: blue;">C.ACCOUNT_SEG5_CODE PROJECT,</span><br />
<span style="color: blue;">C.ACCOUNT_SEG6_CODE INTERCOMPANY,</span><br />
<span style="color: blue;">C.ACCOUNT_SEG7_CODE GEOGRAPHY,</span><br />
<span style="color: blue;">C.ACCOUNT_SEG1_CODE FUTURE2,</span><br />
<span style="color: blue;">A.MCAL_PERIOD_NAME,</span><br />
<span style="color: blue;">B.BALANCE_ACCT_AMT,</span><br />
<span style="color: blue;">B.BALANCE_LOC_AMT</span><br />
<span style="color: blue;">from</span><br />
<span style="color: blue;">W_MCAL_PERIOD_D A,</span><br />
<span style="color: blue;">W_GL_BALANCE_F B,</span><br />
<span style="color: blue;">W_GL_ACCOUNT_D C</span><br />
<span style="color: blue;">WHERE A.ROW_WID = B.BALANCE_DT_WID</span><br />
<span style="color: blue;">AND B.GL_ACCOUNT_WID = C.ROW_WID</span><br />
) T2060495<br />
group by T2060495.COMPANY<br />
order by c2<br />
<br />
<br />
So what OBIEE does is that it gets the result from each query individually, then at the server level, it merges the 2 dataset into one report..<br />
<br />
The caveat here is that, if each query brings back huge data set, then this technique should be used with caution as it may kill performance during the merging..<br />
<br />
Thanks<br />
<br />
Until next timeBojiehttp://www.blogger.com/profile/13163354520521690612noreply@blogger.com2tag:blogger.com,1999:blog-8502142627599800928.post-22432769298497142352014-06-25T16:47:00.001-07:002014-06-25T16:47:24.823-07:00How to update report and automatically store updated by and updated time through writeback in OBIEE<span style="font-family: Arial, Helvetica, sans-serif;">Today I am going to talk about writeback in OBIEE. As you know, writeback is a feature in OBIEE that allows users to update reports that they see in OBIEE without having to deal with the ETL process and reloading table. </span><br />
<span style="font-family: Arial, Helvetica, sans-serif;"><br /></span>
<span style="font-family: Arial, Helvetica, sans-serif;">There can be reasons why users would want to be able to update the records they are seeing in the report. There could be a known error that users are aware of in their accounting system that is producing the wrong Journal Entries that they are seeing, long term solution will be to correct the data from the system, but for auditing purposes at quarter-end, they are just going to manually update the record through OBIEE report so that the numbers will balance out. </span><br />
<span style="font-family: Arial, Helvetica, sans-serif;"><br /></span>
<span style="font-family: Arial, Helvetica, sans-serif;">Another user case can be to have users manually update their username and password in OBIEE on their own. I know that in big enterprises, the user credentials are typically synchronized with their other credentials so it would not likely to have such requirement to let users change their passwords, but in some cases where you may have external users who are not part of the company, they may want to have their own password that can be changed. They can do so by using writeback against the user table to have username and password fields updated.</span><br />
<span style="font-family: Arial, Helvetica, sans-serif;"><br /></span>
<span style="font-family: Arial, Helvetica, sans-serif;">Anyways, the set up of writeback is pretty standard, which I am not going to get in too much details. Google around and you will find many articles that shows you how to do so. Basically, it comes down to the following steps:</span><br />
<span style="font-family: Arial, Helvetica, sans-serif;"><br /></span>
<span style="font-family: Arial, Helvetica, sans-serif;">1.you update the instanceconfig.xml file to enable writeback feature. </span><br />
<span style="font-family: Arial, Helvetica, sans-serif;"><br /></span>
<span style="font-family: Arial, Helvetica, sans-serif;">2. Enable writeback on the columns you want in rpd (BMM Layer)</span><br />
<span style="font-family: Arial, Helvetica, sans-serif;"><br /></span>
<span style="font-family: Arial, Helvetica, sans-serif;">3. Give permissions to the application roles to have writeback, done in RPD. (Check permission identity manager and presentation column)</span><br />
<span style="font-family: Arial, Helvetica, sans-serif;"><br /></span>
<span style="font-family: Arial, Helvetica, sans-serif;">4. Create writeback template and save it as writeback.xml file and put it in following directory in the obiee server machine <span style="font-size: 11pt; line-height: 107%;">/opt/app/OBIEE/instances/instance1/bifoundation/OracleBIPresentationServicesComponent/coreapplication_obips1/</span><span style="font-size: 11pt; line-height: 107%;">analyticsRes/customMessages/</span><span style="font-size: 11pt; line-height: 107%;"> </span></span><br />
<span style="font-size: 11pt; line-height: 107%;"><span style="font-family: Arial, Helvetica, sans-serif;"> </span></span><div>
<span style="font-size: 11pt; line-height: 107%;"><span style="font-family: Arial, Helvetica, sans-serif;">5. Unable writeback in presentation service by checking the setting in column property</span></span></div>
<div>
<span style="font-size: 11pt; line-height: 107%;"><span style="font-family: Arial, Helvetica, sans-serif;"><br /></span></span></div>
<div>
<span style="font-size: 11pt; line-height: 107%;"><span style="font-family: Arial, Helvetica, sans-serif;">6. Define writeback template name in table properties (Only Table view setting)</span></span></div>
<div>
<span style="font-size: 11pt; line-height: 107%;"><span style="font-family: Arial, Helvetica, sans-serif;"><br /></span></span></div>
<div>
<span style="font-size: 11pt; line-height: 107%;"><span style="font-family: Arial, Helvetica, sans-serif;">So here I am only going to talk about step 4, how to create writeback template so that not only it updates the columns, but also it automatically update the username and timestamp when any user executes the writeback.</span></span><div>
<br /></div>
</div>
<div>
In my case I have 7 update-able columns, and I have created 3 columns to help with the writeback: last_updated_by, last_updated_date, record_ID. Last_updated_by will store a default system generated value initally and Last_Updated_Date will store the system timestamp of the day that data was loaded to the table through ETL. Record_ID is like Row_ID, it is a unique identifier of each row of the table. </div>
<div>
<br /></div>
<div>
With the requirement, I am first creating my report in OBIEE <div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjQbJK-6P7H_aPIANSXBpR_Q3ipxDRKzXkHnVixkJChX-T-UueN_JXqaz-v4O_SbYNZDaP4Oh7Y6AMrOe-JGadlmoebSEqRaDcUwoKj5kzZbHDVuCiAuverVrj4O1_VOqkmxsM0J0S-S6U/s1600/writeback1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjQbJK-6P7H_aPIANSXBpR_Q3ipxDRKzXkHnVixkJChX-T-UueN_JXqaz-v4O_SbYNZDaP4Oh7Y6AMrOe-JGadlmoebSEqRaDcUwoKj5kzZbHDVuCiAuverVrj4O1_VOqkmxsM0J0S-S6U/s1600/writeback1.jpg" height="252" width="640" /></a></div>
</div>
<div>
<br /></div>
<div>
Noticed I added a new column called 'User'. The expression of this column is 'VALUEOF(NQ_SESSION.USER)', this gives you the user ID of the user who is using the report (and of course, who is likely going to update record next). If you have successfully configured user authentications in rpd, you will have this session variable, it could be named differently in your environment. </div>
<div>
<br /></div>
<div>
In Advance Tab, you will find the XML code for this report, there you will pick up the column ID for each of the columns you are interested. I will show you an easy way to identify the column IDs:</div>
<div>
<br /></div>
<div>
So here is a part of the entire XML code:</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiFxORuZn-W3N_2wK1vU7_sBTnOHdNvlWxBEpgtpdyl_QOkl4crV-_wcm4H_mDhfwXWoWU6chas2Ea5nX87wnLLv1QYyGMtjV_Kg_G2P148He1aNW9kK5dydmFKupurjeFa8KnayKeipY8/s1600/writeback2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiFxORuZn-W3N_2wK1vU7_sBTnOHdNvlWxBEpgtpdyl_QOkl4crV-_wcm4H_mDhfwXWoWU6chas2Ea5nX87wnLLv1QYyGMtjV_Kg_G2P148He1aNW9kK5dydmFKupurjeFa8KnayKeipY8/s1600/writeback2.jpg" height="379" width="640" /></a></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
Now if I want to find the column ID of Record ID, I will then do control+F and type 'Record ID' in my search box. You will find the column name highlighted by the computer search, then find the column_ID right above this column name, it will be the column ID of this column name:</div>
<div>
<br /></div>
<div>
Note: Record_ID is the physical column name and Record ID is the displayed column name that I made:</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqdSlDpKXMlQH8u8HIE42AyA6wSns03Z9yGWWUazvcj6FnyuAgeX7ggD8RST0xkzJyV1MAgn8pIZlCOEYrEeuBuftUItTNEA4iIkkqqeZFtWShKMyx2qeTYzXUtqWi54ym9RoLjcETN5U/s1600/writeback3.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqdSlDpKXMlQH8u8HIE42AyA6wSns03Z9yGWWUazvcj6FnyuAgeX7ggD8RST0xkzJyV1MAgn8pIZlCOEYrEeuBuftUItTNEA4iIkkqqeZFtWShKMyx2qeTYzXUtqWi54ym9RoLjcETN5U/s1600/writeback3.jpg" height="416" width="640" /></a></div>
<div>
<br /></div>
<div>
Using this method, I have found all of the column IDs of the columns that I am interested. </div>
<div>
<br /></div>
<div>
Now let's start writing the writeback template:</div>
<div>
<br /></div>
<div>
<br /></div>
<div>
Template:</div>
<div>
<br /></div>
<div>
<div>
</div>
<div>
<br /></div>
<div>
<webmessagetables xmlns:sawm="com.siebel.analytics.web/message/v1"></webmessagetables></div>
<div>
<br /></div>
<div>
<webmessagetable lang="en-us" system="WriteBack" table="Messages"></webmessagetable></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<webmessage name="Name"> <b>------ The name of your template, you name it however</b></webmessage></div>
<div>
<br /></div>
<div>
<xml></xml></div>
<div>
<br /></div>
<div>
<writeback connectionpool="Write Back"> <b>---- This is the name of connection pool that your physical table </b><div class="separator" style="clear: both; text-align: center;">
</div>
</writeback></div>
<div>
<br /></div>
<div>
<insert> SELECT NULL from DUAL </insert> </div>
<div>
<update><b> UPDATE fa_table SET JE_CATEGORY = '@{c4532bb3c8bf3fa07}', COST_CENTER = '@{c2133b77cfebd0a7c}', PRODUCT = '@{cfd5c9816d8ca43fa}' ,</b></update></div>
<div>
<b>PROJECT = '@{c06bafe4ddddd622e}', GEOGRAPHY = '@{c55fdf58fcd9b827e}', FUTURE2 = '@{c44356274c12d521f}', YES_NO = '@{c1e7217420772545b}', LAST_UPDATED_BY = '@{cf5dd00aa4eeabdf3}',</b></div>
<div>
<b>LAST_UPDATED_DATE = sysdate</b></div>
<div>
<b>WHERE RECORD_ID= @{c70c57d49eba128a6}</b> </div>
</div>
<div>
<br /></div>
<div>
</div>
<div>
<br /></div>
<div>
</div>
<div>
<br /></div>
<div>
</div>
<div>
<br /></div>
<div>
</div>
<div>
<br /></div>
<div>
</div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgqY1vbMlkmuISK1d0jWE-apgKyfGdDLMkJg5_P8lvIblSnNmG-rAI2WGEd3SL2vzx6kT-bZTuKOCDCkKgT49yQIYqJhzkhyphenhyphennue5uhIrAJKJ3x_QhByLegU8B-xNcVqjpFa9qwDuFXy4EE/s1600/writeback4.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgqY1vbMlkmuISK1d0jWE-apgKyfGdDLMkJg5_P8lvIblSnNmG-rAI2WGEd3SL2vzx6kT-bZTuKOCDCkKgT49yQIYqJhzkhyphenhyphennue5uhIrAJKJ3x_QhByLegU8B-xNcVqjpFa9qwDuFXy4EE/s1600/writeback4.jpg" height="242" width="640" /></a></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
Let's talk about this part:</div>
<div>
<br /></div>
<div>
<div>
<b>UPDATE fa_table SET JE_CATEGORY = '@{c4532bb3c8bf3fa07}', COST_CENTER = '@{c2133b77cfebd0a7c}', PRODUCT = '@{cfd5c9816d8ca43fa}' ,</b></div>
<div>
<b>PROJECT = '@{c06bafe4ddddd622e}', GEOGRAPHY = '@{c55fdf58fcd9b827e}', FUTURE2 = '@{c44356274c12d521f}', YES_NO = '@{c1e7217420772545b}', LAST_UPDATED_BY = '@{cf5dd00aa4eeabdf3}',</b></div>
<div>
<b>LAST_UPDATED_DATE = sysdate</b></div>
<div>
<b>WHERE RECORD_ID= @{c70c57d49eba128a6}</b></div>
</div>
<div>
<b><br /></b></div>
<div>
fa_table is the physical table name. The following 7 columns are to be update-able by users:</div>
<div>
<br /></div>
<div>
JE_CATEGORY, COST_CENTER, PRODUCT, PROJECT, GEOGRAPHY, FUTURE2, YES_NO</div>
<div>
<br /></div>
<div>
So each of these columns are equal it's column ID.</div>
<div>
<br /></div>
<div>
Now column LAST_UPDATED_BY = <b>cf5dd00aa4eeabdf3. </b>This column ID is not for last_updated_by, it's actually the column ID for column USER. The column ID of last_updated_by is never used.</div>
<div>
<br /></div>
<div>
The idea here is that last_updated_by will be updated with the value of column USER, which is the username of the session. </div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhN_xXOu0gpb5B8tB3BfpG_l5QL0xaw03BEIlqXix8OLlspZI423LSmhdc08Vx14Vc40mNRbak76okcQb_DiCFxO1L3px_9lc9w5pq-AhqCfNGuuSVxneDXeBhlQSO9S7dbptJYxVUjNyA/s1600/writeback5.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhN_xXOu0gpb5B8tB3BfpG_l5QL0xaw03BEIlqXix8OLlspZI423LSmhdc08Vx14Vc40mNRbak76okcQb_DiCFxO1L3px_9lc9w5pq-AhqCfNGuuSVxneDXeBhlQSO9S7dbptJYxVUjNyA/s1600/writeback5.jpg" height="196" width="640" /></a></div>
<div>
<br /></div>
<div>
Now last_Updated_Date = sysdate. This means whenever user is updating the record, it's happening at the present moment, therefore this column will be updated with whichever date the update happens.</div>
<div>
<br /></div>
<div>
In the where clause I am using record_id as the criteria because I want to ensure that when user makes an update, the update is only applied to 1 row, not a whole bunch of rows. Without record_id in the where clause, then when user updates cost center record, then all of the rows with the same cost center name that the user is trying to modify will get updated. </div>
<div>
<br /></div>
<div>
After putting this writeback.xml in the right place and restarting OBIEE, let's go back to the report. Here it is import to uncheck the writeback on column last updated by and last updated on:</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiufyHtCxydnhVc4F2akxwle2M0Tzj3bUfoQz461h-DWrKuJL7kgcAdg1xRaYeYyZpb7ICOgEQhTwH9qXBOikhNSCybOAshpbSuFeDPkQ2PLGDRec5W6VTiIGA074U9eAtKYbsVCJ9DP-0/s1600/writeback6.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiufyHtCxydnhVc4F2akxwle2M0Tzj3bUfoQz461h-DWrKuJL7kgcAdg1xRaYeYyZpb7ICOgEQhTwH9qXBOikhNSCybOAshpbSuFeDPkQ2PLGDRec5W6VTiIGA074U9eAtKYbsVCJ9DP-0/s1600/writeback6.jpg" height="222" width="640" /></a></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
Define the template name in the table view properties:</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjHTOytaHMgeQokhe0KbvZtGR5gFq0Ire_ySkjVSvf8Yh_OISndgCYMzmhagN8ePeqKYGVDeOuelpJSiSWnr13qgrXmT593diFWt193tD62o0IpQOzLaEa4-itOUKKQsKclMC1t51CW-J8/s1600/writeback7.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjHTOytaHMgeQokhe0KbvZtGR5gFq0Ire_ySkjVSvf8Yh_OISndgCYMzmhagN8ePeqKYGVDeOuelpJSiSWnr13qgrXmT593diFWt193tD62o0IpQOzLaEa4-itOUKKQsKclMC1t51CW-J8/s1600/writeback7.jpg" height="410" width="640" /></a></div>
<div>
<br /></div>
<div>
Now let's run the report and make some updates:</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgMo8H-TinwLOfdCr9A-UPYpIvFki5cKvJ3X8tLOWYqwZLPnqROg5hTKlmxIAaREyhWQNEIqAKZq3c7ACpuBcQuC94spaYTVAQb_WYbqMqGaQ0jkqo9xFdDaqux89XJfwJ9jxUBMJLyYms/s1600/writeback8.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgMo8H-TinwLOfdCr9A-UPYpIvFki5cKvJ3X8tLOWYqwZLPnqROg5hTKlmxIAaREyhWQNEIqAKZq3c7ACpuBcQuC94spaYTVAQb_WYbqMqGaQ0jkqo9xFdDaqux89XJfwJ9jxUBMJLyYms/s1600/writeback8.jpg" height="346" width="640" /></a></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
Updating JE Catogory with random stuff:</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgbheb9cS8CVntjg7z52DsjhzQUUUKtzUY9FXVccT68hQys6b5bK88m3gEcXrVGx1fkRfgWN7RaAXj3ihhtN-RQFzSaJak7Jpx9IuEkJ3KJYYVWiRintJIzYp84WxJvMlA29niDCs1fU0A/s1600/writeback9.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgbheb9cS8CVntjg7z52DsjhzQUUUKtzUY9FXVccT68hQys6b5bK88m3gEcXrVGx1fkRfgWN7RaAXj3ihhtN-RQFzSaJak7Jpx9IuEkJ3KJYYVWiRintJIzYp84WxJvMlA29niDCs1fU0A/s1600/writeback9.jpg" height="320" width="640" /></a></div>
<div>
<br /></div>
<div>
After done and the record is updated. Moreover the last_updated_by column is automatically updated with my username.</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi75zxxzy_dQKSA92J-HEEnecwkUmIoSzqBkMFYkdka5nZBT5Mp48zhM0zzQP1kfukmHPUdsM2sy-aEgQIDGQo9RzcmZ9OMkmeWgPriKv5DZC_SIQ18DRZ6VQzXd_SGYEmhnthkK-UG_v0/s1600/writeback10.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi75zxxzy_dQKSA92J-HEEnecwkUmIoSzqBkMFYkdka5nZBT5Mp48zhM0zzQP1kfukmHPUdsM2sy-aEgQIDGQo9RzcmZ9OMkmeWgPriKv5DZC_SIQ18DRZ6VQzXd_SGYEmhnthkK-UG_v0/s1600/writeback10.jpg" height="348" width="640" /></a></div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div>
<br /></div>
<div>
Now it is important to sort the report based on record ID column. Because every time you update a text field with different value, the row will be re-sorted because text field is always going alphabetically. To avoid having your updated record 'disappeared' on you after you save, just sort your report by record ID.</div>
<div>
<br /></div>
<div>
Thanks and until next time.</div>
<div>
</div>
Bojiehttp://www.blogger.com/profile/13163354520521690612noreply@blogger.com9tag:blogger.com,1999:blog-8502142627599800928.post-90818510073788342302014-06-03T15:55:00.000-07:002014-06-03T15:55:00.943-07:00About Merging 2 rpd in OBIEE -- The only article that mattersHello All<br />
<br />
About merging rpds. You all know this merge button under 'file' in Admin tool right?<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhAmQOyjJDIJvJXpgCLn1-HvyXcDuaiEX3ZeMhHL4Y-r_mALqxaety00WX65ik0QhXshYle_1bcJzN5nXC-ecQjgLw2qVyOUs2j4-zFeHqp9Il1rBzt_5ZxTkx_L9N_PXbqx-FAWhdn224/s1600/Merge1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhAmQOyjJDIJvJXpgCLn1-HvyXcDuaiEX3ZeMhHL4Y-r_mALqxaety00WX65ik0QhXshYle_1bcJzN5nXC-ecQjgLw2qVyOUs2j4-zFeHqp9Il1rBzt_5ZxTkx_L9N_PXbqx-FAWhdn224/s1600/Merge1.jpg" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
This launches the merge repository wizard, which is easy.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
The confusing part is, how to determine original master rpd, Modified rpd, Current Rpd?</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
This is what I do to remember:</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Let's say there is a scenario that I have RPD1 and RPD2. I want to merge these 2 rpds so that there will be a new RPD that has both RPD1 and RPD2 objects. </div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
In this case (or any case), you need to create a new rpd with nothing in it. basically a dummy rpd, call it RPD(Dummy)3.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Now open either RPD1 or RPD2, which one doesn't matter, but once it's opened, use the merge button from that rpd to start merging. Therefore, the rpd that is currently opened (so that you can click 'merge' button from it) will automatically be your 'Current' RPD.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Pick RPD(Dummie)3 as your original master rpd and the other RPD (RPD 2 or RPD1) as your modified RPD, then merge..</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
To make it simple, when merging RPD1 and RPD2 into RPD3, this is the identification in the merging wizard:</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Dummy RPD (empty RPD made by you ) = Original Master RPD</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
RPD1 or RPD2 = Current RPD</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
RPD2 or RPD1 = Modified RPD</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Save Modified RPD AS: Give the resultant RPD a name and location as you wish = RPD4.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
RPD4 becomes the finishing product of this merge and you can leave Dummy rpd there for your next merge.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
In the merging strategy step, if you are ever in doubt whether to select 'Current' or 'Modified', just see which rpd did these red objection come from.</div>
<div class="separator" style="clear: both; text-align: left;">
In my case, all of the are from 'Current Repository', so in decision list, I pick current for all.</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhJyyNTyZmMYeNcYoHD6f40Y0uU-tFonLJSPpparO2ZPO-SrKEYAWKFDLXlSuoM4nCyV4regGne4ewXnx29K8mDgwQw9WP45-jAGzdfnYBBhPUEOA6LV3SGwfel21UdcXeIzd0vPNddiyM/s1600/Merge2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhJyyNTyZmMYeNcYoHD6f40Y0uU-tFonLJSPpparO2ZPO-SrKEYAWKFDLXlSuoM4nCyV4regGne4ewXnx29K8mDgwQw9WP45-jAGzdfnYBBhPUEOA6LV3SGwfel21UdcXeIzd0vPNddiyM/s1600/Merge2.jpg" height="426" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Let the application take care of the rest after you hit 'Finish' when you are finished selecting decisions.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Note:</div>
<div class="separator" style="clear: both; text-align: left;">
The merging of rpds can take a while. If one of the RPD is 20MB, the whole merging process can take up to 3 hours.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Try to have 2 rpds that are completely unrelated with each other when merging. A lot of time, merging is unreliable, specially in this version, you never know what has gone missing after merging. If RPD1 and RPD2 have absolutely nothing in common, then the merging of the two might minimize the risk of conflict during merge. If both rpds have a lot in common, I don't recommend merging. Its easier to manually reconcile the difference than to merge.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Thanks </div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Until next time</div>
<br />Bojiehttp://www.blogger.com/profile/13163354520521690612noreply@blogger.com0tag:blogger.com,1999:blog-8502142627599800928.post-2846518139880720892014-03-04T14:38:00.002-08:002014-03-04T14:38:46.506-08:00Evaluting BI tools: Compare OBIEE with open source reporting toolsHaving worked for various BI projects, often it comes to time to make decision on what tool or technology to use to implement the requirements. Often, the company has a great vision knowing where they want to go and what roadmap they want to follow, but they don't know what technology will be the best for them to achieve their goals and meet their budget. So many OBIEE projects are having difficulties and issues because from the beginning, OBIEE simply wasn't the most ideal tool for what the clients want to do. Not saying OBIEE can't do certain things (actually, there are certain things they can't), it's just that the complexity, resources and time that would involve don't always match the expectation that the clients had in mind during POCs. Therefore, It is very important to first understand that POC and real life implementation are too different things, I am just going to be as honest as I can be for the benefit of all.<br />
<br />
Any tools can look great during POC where you don't have real data and real scalability issues. Every problem can be solved 'in theory' if the simple POC works. Look at T.V commercials, advertisements, these are the work of the best marketing genius of the world. They know how to make things look great on T.V, in ads and in demo, which is just a variation of the 'prove of concept' for general consumers. What happen after you blow your wallet for some magical weight-loss pills or some total package home care system, or even a weekend self-help workshops that promises to change your life? Then you realize that things aren't as good as what you see at first site. The bottom-line, POC is just a simple sales demo, which is not restricted to any specific environments against specific wants and needs. Now it is the buyer's responsibility to know how to apply that to their specific requirements and making it work. Anyways, enough of that.<br />
<br />
Now lets look at what are some of the things that OBIEE isn't very efficient at doing compared to some other open source java reporting tools:<br />
<br />
1. The implementation of localization and internationalization of OBIEE reports isn't very straightforward. There are blogs and documents out there that explains how to do such things in OBIEE, but go through them and try to implement it yourself. You will see that it does take a lot of work. Now integrating it with other home-grown java applications while maintaining the OBIEE local languages and time zones in Synch with that application is another thing. I am not saying it can't work, but it is complicated and it does take time and resources (Money.) The same thing can be done fairly easily in tools like Jasper.<br />
<br />
2. New reporting requirements from the business users can't always be fulfilled right away. Oftenly, the requirements are identified from the OLTP side, but it is going to go through an ETL process and OBIEE configuration process before it can be made available for reporting. Several places can cause issues and there are many interdependencies that can cause data issues or performance issues. Most of all, this is not something you can get back to the user in 2 hours with new reports up and running. It requires development cycle. At the same time, in reporting tool like Jasper, you can develop these requirements directly by writing PL/SQL packages against the OLTP system, it can be a quick thing if you have a good & healthy framework<br />
<br />
3. OBIEE has a high cost. Here, we are not just talking about the cost of OBIEE application itself (which is already pretty costly). You have to decide what ETL tool to use to work with OBIEE, if you happen to pick Informatica, then great, that's another high expenses. On top of that, you are going to need more resources, data architects, ETL developers, OBIEE developers, DBA and maybe more guys. That can be an expensive team right there. On top of that, you are not just going to have one machine that takes care of all these projects, you are going to need more machines, more hardware & software resources. That's another expense. Now, how do you feel after spending all these money but have to wait for development cycles (with high probability of delays and issues) to get job 1 and 2 done, while you see some other projects that use less than 1/50 of sources than yours but can get the requirement more agilely and dynamically done in much less time?<br />
<br />
4. OBIEE is less flexible, less customizable (graphs, look and feels and other programmable integration features). Requirements can come in various ways. Not all of the OBIEE features are created to satisfy them. That's why there are always new enhancement request being made against the product, but it's up to the future release of the product to decide which ER can be fulfilled. If you are currently implementing your project, don't count on future releases. Open source reporting tools are much more flexible. These tools are good for people that knows how to program, how to mess around. It's like Iphone vs Android (oops I said it again) .<br />
<br />
5. OBIEE works strictly on relational data models. In order for OBIEE to work, your BMM layer needs to have your logic tables set up and arranged according to standard star-schema. This can be a big restriction if your requirement is very complex that doesn't translate to standard star schema. Often, you will have to take the data fields from the source and design your pre-computed aggregate tables in order to make OBIEE's job easier, in other words, the complexity goes to ETL layer. Then again, why would I spend too much money to have OBIEE does easy stuffs where pre-computations are spoon fed to you? In open source tools or some other BI tools like Business Object, it is much more flexible in terms of what data model can be worked with directly with reporting.<br />
<br />
The above are some of the most common areas where the OBIEE project has difficult challenges, specifically when expectation is not met. So before you get into OBIEE, you have to know where you are getting into to best avoid the difficult situations later on.<br />
<br />
In my opinion, the value of OBIEE lays in the solution being delivered to you. Take BI Apps for instance, it is the best example of how OBIEE can be advantageous when the entire framework of solution is already provided to you after years of research of various business scenarios. The package, which includes DB schemas, ETL framework, reports and dashboards are already designed and engineered with the best solution in the most optimized way, can save clients tons of money and time for getting the information they desire. Imagine how it's going to be if you have to do your own research, requirement gathering, design and engineering of BI solution? Well, unless you are a genius :)<br />
<br />
Another advantage of OBIEE is that it is easier to get into. Since the programming aspect of it is minimum, a lot of the education are conceptual based. Therefore, OBIEE can be favored by those who wants to rely on some object oriented tools to get the job done, instead of doing their own coding on a black screen. Jasper is more for programmers, it requires good understanding and design of the DB model to help optimize the report queries, or it can run very slow. For OBIEE, the performance can be much faster with standard star-schema designs (the price to pay is the time spent on ETL development cycle).<br />
<br />
There is no perfect tool, it's up to the individuals, from company visionaries to architects, from engineers and project managers to make things work perfectly. But knowing the limitation of each tools, I hope it can drive future improvement of these technologies, but more importantly, help the decision makers to make the right decision from the beginning.<br />
<br />
ThanksBojiehttp://www.blogger.com/profile/13163354520521690612noreply@blogger.com12tag:blogger.com,1999:blog-8502142627599800928.post-18168267890729794852014-02-18T18:37:00.004-08:002014-02-18T18:37:55.486-08:00Interview for OBIEE position and how to succeedHello All<br />
<br />
Today's topic is everybody's favorite, that is about interviews. Interview is a pain in the ass and it is for a lot of people. The more senior you are (or you think you are), the more you dislike interviews. Doing the work and talking about your work are completely two different things. When you are at work, you may run into challenges, but at least you don't have to come up with a working solution in the next 30 minutes if you can't. However in interviews, you are expected to provide proper responses in 5 mins. Therefore, if you want to excel at interviews, you should prepare yourself on your emotional intelligence, not so much on memorizing the things you can google.<br />
<br />
Some of the mostly overlooked questions are:<br />
<br />
Tell me a little bit about yourself, in particular, your experience related to OBIEE, BI Apps, Informatica, project management etc.<br />
<br />
What is the most challenging problem you have encountered in your past experience with OBIEE, BI Apps, Informatica, project management etc, and how you solved that problem.<br />
<br />
What is your approach to _____?<br />
<br />
See, these are open-ended questions, depending on how you respond, the interviewer can throw anything at you. But first and foremost, it is important to prepare a well structured response to all these questions before any interviews. You don't have to memorize your speech word for word, but you do need to follow your structure. Over time, based on your growing experience of interviews, you might refine your speech.<br />
<br />
Oftenly, interviewers aren't much better than you either. However, they are in the position to make decision, therefore, you need to learn to interact with them properly.<br />
<br />
Sometimes, interviewers make random comments at your responses that may affect you emotionally. You were describing how great your previous project was, or how complex your design was and then here he comes with 'That's not a big deal.' or 'Aren't you over-complicating things? ' or even 'How is that going to work?'. Suddenly, you feel you have been disrespected, looked down on or misunderstood. This is very common among experienced job candidates. The more experience you have, the more likely you will resist other's criticism, especially in the engineering fields where people rarely worked on people skills. You have been through so many rounds of technical discussions at work, you are so used to arguing with your coworker and you always win, therefore, you can't handle when people don't show appreciation of how great you think you are during interviews. Your subconscious will look at this interaction just as another technical discussion or even a 'political fight' in the office. You start raising your voice and so does the interviewer. At the end, you may win the argument, but lose the interview; or this verbal exchange will leave a significant mark on your psyche that after that all you are doing is mumbling to yourself with 'this guy is an asshole' and you totally forget about the fact that the interview isn't over.<br />
<br />
Over the years of interacting with engineers and managers, I have realized something when it comes to technical discussions. If it occurs during the interview, it's easy. Just answer the question with whatever you know, don't worry about trying to convince the other person. If the interviewer is saying something you don't like, just take it easy. Reply with 'ok, that's interesting you said it', 'ok, I see your point' or 'What you said also make sense, I think we can definitely discuss this in more details off line.'. For God sake, just get over with this question and go to the next, if the interviewer wants to show off his knowledge, that's even better, let him do all the talking, he might enjoy so much that he would hire you based on that. Now of course, if you are applying for management, sales or customer service jobs, that's different. For engineering jobs, the less you talk the better it is.<br />
<br />
In today's world, the competition in IT is getting fierce, especially in niche markets like OBIEE, Hadoop, Salesforce, Tibco where there are plenty of candidates, the interviewers rarely ask questions where you can google the answer anymore. They want to find out whether you really have experience or not. Therefore, a lot of questions are open-ended, opinion based questions. While it is important for you to prepare your speeches, it is just as important to realize that there is nothing wrong with you if you don't get the job after.<br />
<br />
Everything happens for a reason. How you perform during your interview is always an accurate reflection of where you stand in the job market and your relationship with this particular interviewer. There is no surprise no matter what the outcome is. Take it easy. Try to learn something from the interview if there is anything to be learned, remember the questions that you couldn't answer and research on it afterwards. Do what you can to improve your chance for the next interview. If you fail, don't take it too seriously. I have worked in this field for long enough time that I personally can tell you that you can rejected for all kinds of reasons that it might even sound ridiculous to you. Just chill out, don't think about the interviews after it's done.<br />
<br />
Hopefully, after working in the field for long enough time, you build up your personal network well enough that opportunities will get referred to you rather than you having to apply and compete for it. Building contact is one of the most important things to do in life and that's how you can minimize your chance of getting interviews in the future.<br />
<br />
Until then:<br />
<br />
<br />
<br />
<br />Bojiehttp://www.blogger.com/profile/13163354520521690612noreply@blogger.com1tag:blogger.com,1999:blog-8502142627599800928.post-26939031806728024272014-02-01T15:37:00.000-08:002014-02-01T15:37:00.325-08:00Create Pixel Perfect Report using BI Publisher that includes charts, tables and dynamic SQL queryHello<br />
<br />
This time I want to share with you how to create a pixel perfect BI Publisher report out of an existing dashboard that includes different type of reports.<br />
<br />
When it comes to reporting, the BI Dashboard is highly interactive, it allows navigation, ad-hoc query and other dynamic features. That's the value of OBIEE. However, sometimes when you want to send the results with pdf or other formats, it is not so pixel perfect. In some areas people don't mind, but in a lot of inter-company communications, the version has to be pixel perfect. I mean, have you ever seen an invoice letter from your bank that's components are out of proportion? This is why BI Publisher has been integrated into the OBIEE platform.<br />
<br />
Take a look at the following dashboard:<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4ULZMqQYYqjfEnB398Uk0SUiqZOTWphBd2OCIludVXxskl-_YHnILwEyQXGlWer3OkzYCwuv_V8n3RXBH9yjQBGq-9-JMMFNik74atsN_tUD1OQvxvEFusj_DTLGwCYltY7YNYWtViIU/s1600/BIP1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4ULZMqQYYqjfEnB398Uk0SUiqZOTWphBd2OCIludVXxskl-_YHnILwEyQXGlWer3OkzYCwuv_V8n3RXBH9yjQBGq-9-JMMFNik74atsN_tUD1OQvxvEFusj_DTLGwCYltY7YNYWtViIU/s1600/BIP1.jpg" height="292" width="640" /></a></div>
<br />
<br />
It has multiple charts, tables and texts arranged in a articulated way. There are even reports created using direct SQL query:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjpCMipSHTVprZrwZyY47ttRjBLZ8WrcPcwwWi76zhELRc5BU6NEexhF8gZqPmHp70fQD8Yg4_Yv6EIc-TSMngSACAzVW8IRmcp5dCz_Wni8-GKlXxVMR-DtZcc5ja57I-2-zE_qU1j2GA/s1600/BIP2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjpCMipSHTVprZrwZyY47ttRjBLZ8WrcPcwwWi76zhELRc5BU6NEexhF8gZqPmHp70fQD8Yg4_Yv6EIc-TSMngSACAzVW8IRmcp5dCz_Wni8-GKlXxVMR-DtZcc5ja57I-2-zE_qU1j2GA/s1600/BIP2.jpg" height="274" width="640" /></a></div>
<br />
<br />
So reports like the following are regular reports displayed in charts or tables:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-db7WNYprxDA30D5X2qY89yIk4nPRIOkd1k-exxw_j-VoKSQyIrl23LJuB-ewghltvjCwFleW6tTJx088d0GAgwkBitULzxAxEn8wwGtpBcPCvwJcoXjiYlp0NpeDtbhbEGD9hP4ujmE/s1600/BIP4.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-db7WNYprxDA30D5X2qY89yIk4nPRIOkd1k-exxw_j-VoKSQyIrl23LJuB-ewghltvjCwFleW6tTJx088d0GAgwkBitULzxAxEn8wwGtpBcPCvwJcoXjiYlp0NpeDtbhbEGD9hP4ujmE/s1600/BIP4.jpg" height="240" width="640" /></a></div>
Some reports like the following are created using direct SQL with presentation variables passed from the dashboard prompt:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4b6gHUglx-qNRtTRRXqrzbgnRkdRfhetbzRp6XJ3AYKUrOGhU64J7sjUQ1Z_h6etm2K_OQG3IDvcb1ygZlUgA19am1MX37829OJ5_1hli_nkZNpbvV68k_QClodycwSwxM8umj8s5hQM/s1600/BIP5.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4b6gHUglx-qNRtTRRXqrzbgnRkdRfhetbzRp6XJ3AYKUrOGhU64J7sjUQ1Z_h6etm2K_OQG3IDvcb1ygZlUgA19am1MX37829OJ5_1hli_nkZNpbvV68k_QClodycwSwxM8umj8s5hQM/s1600/BIP5.jpg" height="346" width="640" /></a></div>
<br />
So lets see how we can create the same version using BI Publisher which is pixel perfect when you print it out.<br />
<br />
We will start by creating new data model. In the data model windows, go to diagram and select Oracle BI Analysis for the reports that are created using regular approach. I then locate all of the saved BI analysis that I want to create, just select them one by one.<br />
<div>
<br /></div>
<div>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg2GFdG_nfJ3fRhyphenhyphen_vmka-zgUcUYp8brcizkfMQi28Pae2R2GRXSSddrkpMBMOuCZZGhWHvkpPCcrdWcsupWUS2jTVc716FJ2VDUWnaNtWU43T6J6wICyc71I2Pl2PSxHl6Q03jKyn2zhA/s1600/BIP3.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg2GFdG_nfJ3fRhyphenhyphen_vmka-zgUcUYp8brcizkfMQi28Pae2R2GRXSSddrkpMBMOuCZZGhWHvkpPCcrdWcsupWUS2jTVc716FJ2VDUWnaNtWU43T6J6wICyc71I2Pl2PSxHl6Q03jKyn2zhA/s1600/BIP3.jpg" height="260" width="640" /></a></div>
<div>
<br /></div>
<div>
For the one that are created using SQL query with presentation variable, you need to change the data set to using SQL Query and paste the SQL query into the windows:</div>
<div>
As shown in the screenshot, the syntax of the presentation variable will need to be changed from @{PV} to :PV:</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgUzhFy9mMhIvlG8IjXslrwIVXdQWJUlFkc7shOTX_zFGMGAKhvaCC3hsr1MoP1eFuLph3NGBD2xJOY2h4q1x4QpiB9PH13ye70yFL12HZHR-RmJcOk4PBtmKXNrshE1B2sIloNHPtnRAM/s1600/BIP6.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgUzhFy9mMhIvlG8IjXslrwIVXdQWJUlFkc7shOTX_zFGMGAKhvaCC3hsr1MoP1eFuLph3NGBD2xJOY2h4q1x4QpiB9PH13ye70yFL12HZHR-RmJcOk4PBtmKXNrshE1B2sIloNHPtnRAM/s1600/BIP6.jpg" height="236" width="640" /></a></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
After these are done, close the box and it will automatically ask you to create parameters. Just create the parameters with the same Presentation variable name and give default value:</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLiMNMwhVCLa-1uhyxLSfy747jAT4xIwXM1kamq5jAv63NGJlrAn4D7OvI4jxyRlIK-noDG4-6XE2y82IUcht46PKY7wFVV7XAIccINrpyHp974VurWESaetSkttsEw0H3MPABBq7Mh00/s1600/BIP7.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLiMNMwhVCLa-1uhyxLSfy747jAT4xIwXM1kamq5jAv63NGJlrAn4D7OvI4jxyRlIK-noDG4-6XE2y82IUcht46PKY7wFVV7XAIccINrpyHp974VurWESaetSkttsEw0H3MPABBq7Mh00/s1600/BIP7.jpg" height="332" width="640" /></a></div>
<div>
<br /></div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh3u8uJYNhOa-ZxjI8tSvmRhngjFNK6fZJowIh2Ll4-jOmyy0uwi_inIpnrGPCjAtX-y224uUvzByHxam2cGml3ewSCEB9hUiHx1dm0gQXH9-Vc481FXwecsmU6tPeDNtLuHZON-kYHoF8/s1600/BIP8.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh3u8uJYNhOa-ZxjI8tSvmRhngjFNK6fZJowIh2Ll4-jOmyy0uwi_inIpnrGPCjAtX-y224uUvzByHxam2cGml3ewSCEB9hUiHx1dm0gQXH9-Vc481FXwecsmU6tPeDNtLuHZON-kYHoF8/s1600/BIP8.jpg" height="226" width="640" /></a></div>
<div>
<br /></div>
<div>
After some times, all of the data sets (each one corresponds a BI Dashboard analysis) are create as shown:</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4W66jbnJK_CuNc4x5EWBnBCAMfkCWEwn02qoQ0zmfwxWt7or6gfNmgYT-2NzUWM1mGUpC3nVerCW_GJfPcHES4LsHbh2zTClC6v6uZzmzkJeFX9zDFI3qY44AA1iJC-NliaUfMyPu_Dg/s1600/BIP9.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4W66jbnJK_CuNc4x5EWBnBCAMfkCWEwn02qoQ0zmfwxWt7or6gfNmgYT-2NzUWM1mGUpC3nVerCW_GJfPcHES4LsHbh2zTClC6v6uZzmzkJeFX9zDFI3qY44AA1iJC-NliaUfMyPu_Dg/s1600/BIP9.jpg" height="320" width="640" /></a></div>
<div>
<br /></div>
<div>
Now save the data model:</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgo46oVH6uqn6TXJsEMOFmjl2JZOiK4Mt8m0JfnV5Cbs9ZbD0PX524Y9YfDm-R-Z8LeMTEnpLzx4OisQZx9f5PbaUVrwtC9_jK8kl33RkGlYTZDB9CtbFXvhx1PhdJofNIT3qHl-h9Ii8g/s1600/BIP10.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgo46oVH6uqn6TXJsEMOFmjl2JZOiK4Mt8m0JfnV5Cbs9ZbD0PX524Y9YfDm-R-Z8LeMTEnpLzx4OisQZx9f5PbaUVrwtC9_jK8kl33RkGlYTZDB9CtbFXvhx1PhdJofNIT3qHl-h9Ii8g/s1600/BIP10.jpg" height="304" width="640" /></a></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
Now create new BI Report using the data model that just created as the data source. While creating and going through each windows, it will ask you for layout templates or add tables, just click next and go through them without doing anything:</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_hyVDDf9kVivvZcNhf-59ftsQ82RhvKbmZpJtT8dB4cpZffLZnSjP38HJl2gQ8g-cNv_t_jdOD6dyUNr_6GeDdWFZSaJhg1kmYHOYvRJvum0P_TSzsf55mS-2aY7v_r44OdaTIWuWjOI/s1600/BIP11.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_hyVDDf9kVivvZcNhf-59ftsQ82RhvKbmZpJtT8dB4cpZffLZnSjP38HJl2gQ8g-cNv_t_jdOD6dyUNr_6GeDdWFZSaJhg1kmYHOYvRJvum0P_TSzsf55mS-2aY7v_r44OdaTIWuWjOI/s1600/BIP11.jpg" height="376" width="640" /></a></div>
<div>
<br /></div>
<div>
Then save it:</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgL1INAK4GCeEL3fa79_ZqcrTVfQ_1BZ7FbokD-BdyjlWjiNUACxBAQQ966lZp3f2N8KOXZCL99S3PcmvcoHEJg-V7CPkFDUkAL3Wd_1zPwMOnVT5fQP0vmS7hI-3VKwooqefAa1jX18zA/s1600/BIP12.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgL1INAK4GCeEL3fa79_ZqcrTVfQ_1BZ7FbokD-BdyjlWjiNUACxBAQQ966lZp3f2N8KOXZCL99S3PcmvcoHEJg-V7CPkFDUkAL3Wd_1zPwMOnVT5fQP0vmS7hI-3VKwooqefAa1jX18zA/s1600/BIP12.jpg" height="334" width="640" /></a></div>
<div>
<br /></div>
<div>
Now open the saved report. It is going to run and most likely it will not return anything. But it will allow you to edit report, so do just that:</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPmF_5FBKGKAkaiOR_8emt9iPh-lP3t5RtW6jbbuydDoi1cHdjVy8mNQeZ43V3RnbN2u7NbSAPi1NuF2bnqcmNL8vsEK9Liz9BsjWTw7XjlxjTTs9JGwWHJNTFS-jLoQxaDO0bywLfjC8/s1600/BIP13.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPmF_5FBKGKAkaiOR_8emt9iPh-lP3t5RtW6jbbuydDoi1cHdjVy8mNQeZ43V3RnbN2u7NbSAPi1NuF2bnqcmNL8vsEK9Liz9BsjWTw7XjlxjTTs9JGwWHJNTFS-jLoQxaDO0bywLfjC8/s1600/BIP13.jpg" height="142" width="640" /></a></div>
<div>
<br /></div>
<div>
It will open up a blank template, this is where you can feel free to design your own report arrangements:</div>
<div>
<br /></div>
<div>
One the left side, all of the data set that you created earlier for that data model will be available to be put on the blank sheet.</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIK9jtdGjQK5eZ1PIC9nz6w1RTGQpfWL4X3ET-YN9ecXe5CSPx2o-aaH3axbOn4xMqc6TYu0_0_o-7MfaWFoecNPzkVUh5pAQT3mEfIbGinZ1Ex_adiGiaVxcLjhXDgvMBUf-gngM5E-Y/s1600/BIP14.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIK9jtdGjQK5eZ1PIC9nz6w1RTGQpfWL4X3ET-YN9ecXe5CSPx2o-aaH3axbOn4xMqc6TYu0_0_o-7MfaWFoecNPzkVUh5pAQT3mEfIbGinZ1Ex_adiGiaVxcLjhXDgvMBUf-gngM5E-Y/s1600/BIP14.jpg" height="532" width="640" /></a></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
The insert type on the top of the sheet allows you the insert all kinds of objects, from gauages to tables to charts. I used layout grids to break this blank sheet into different smaller subdivisions and then insert each of the reports into those subdivisions:</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgQ1OBlIGTjeNb_ZXwCe2to2QkR0J1af80tMCMD9yNYlqPze1ZV1pb3pBjOx5lQu-64Er3s-i2NPGZDwGg6727A2XDDDx4F9r6iWB8y-gWdOSXwCJZQKrezYoTphZkhTonw9oksioa81mY/s1600/BIP15.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgQ1OBlIGTjeNb_ZXwCe2to2QkR0J1af80tMCMD9yNYlqPze1ZV1pb3pBjOx5lQu-64Er3s-i2NPGZDwGg6727A2XDDDx4F9r6iWB8y-gWdOSXwCJZQKrezYoTphZkhTonw9oksioa81mY/s1600/BIP15.jpg" height="360" width="640" /></a></div>
<div>
<br /></div>
<div>
The properties on the left side of the sheet allows you to adjust it's size, color and arrangement to make it look good:</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1PET5uE3Yf7DHoUdi-582PvhlhR9-OS84qiLIfga-dEzBpZ0PrQ-tkEvb2DErPTCCdF6gaK6P0RI1AYcaWbqnEsXyn2YP98CZm5r4OU4mtDi02OEru5690ZGmAVa7r9NV_DRulpFBgXM/s1600/BIP16.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1PET5uE3Yf7DHoUdi-582PvhlhR9-OS84qiLIfga-dEzBpZ0PrQ-tkEvb2DErPTCCdF6gaK6P0RI1AYcaWbqnEsXyn2YP98CZm5r4OU4mtDi02OEru5690ZGmAVa7r9NV_DRulpFBgXM/s1600/BIP16.jpg" height="382" width="640" /></a></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
After it's done (these are not technical work, it is more tedious art work), save the report. </div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg6fC-yn97nHLHfOiAvLW8-Itnjle2fGGhorXBHMwFZVkeFyaV7WBt_3O_0VJJocP-80GVWyOg1TdGAdKuSoIBuJ8gWhEsn5mgOjkQXLN49PoawTieO_fwcHyQRJkxIvGN1chgIfMuE3Dg/s1600/BIP17.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg6fC-yn97nHLHfOiAvLW8-Itnjle2fGGhorXBHMwFZVkeFyaV7WBt_3O_0VJJocP-80GVWyOg1TdGAdKuSoIBuJ8gWhEsn5mgOjkQXLN49PoawTieO_fwcHyQRJkxIvGN1chgIfMuE3Dg/s1600/BIP17.jpg" height="500" width="640" /></a></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
Now we can add this report to the original BI Dashboard:</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPTnv1_ZZ-vPyDaDGMl0oXvkzaxOxf5uMkkJKtK6eOjWN2EcJ0k8IOVifzYQYXrYHWc08uloOLji0FvOliyjX98u5i1el2FqrhxMMnO1ERgRjdhUwwxQVazFlw1XgR6UX7wW7dbtU9-FI/s1600/BIP18.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPTnv1_ZZ-vPyDaDGMl0oXvkzaxOxf5uMkkJKtK6eOjWN2EcJ0k8IOVifzYQYXrYHWc08uloOLji0FvOliyjX98u5i1el2FqrhxMMnO1ERgRjdhUwwxQVazFlw1XgR6UX7wW7dbtU9-FI/s1600/BIP18.jpg" height="216" width="640" /></a></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
The BI came be launched here:</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiX44flHuxKebiPpX_-kApDyXtFS4ftn7EHtVY_7AjOloSOuYjhPvcWg165KZyPW38PBDsn6bh4nWU-LkeWCdq2iuMT7BZhfFiazcdhJhPGt3Pn-8a-VbzwEHITflMoNZCTNFUeQEVv3yE/s1600/BIP19.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiX44flHuxKebiPpX_-kApDyXtFS4ftn7EHtVY_7AjOloSOuYjhPvcWg165KZyPW38PBDsn6bh4nWU-LkeWCdq2iuMT7BZhfFiazcdhJhPGt3Pn-8a-VbzwEHITflMoNZCTNFUeQEVv3yE/s1600/BIP19.jpg" height="262" width="640" /></a></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
Now after launching the BIP report, it has to pass the value of the BI dashboard:</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZhNgFV9tkPaVGlNgPuLJ_YHwPFCzVBCUdGHzI_f5z2lnjZydxUg9Nu4xbqMCzwnCsHcf7IbLc9iLE7xhaB4E_p4kAwheUDMXcZ0Mf7O5VrojlPSLWMWR_Gr_qy_ew3rI3Ims18MS8c5E/s1600/BIP20.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZhNgFV9tkPaVGlNgPuLJ_YHwPFCzVBCUdGHzI_f5z2lnjZydxUg9Nu4xbqMCzwnCsHcf7IbLc9iLE7xhaB4E_p4kAwheUDMXcZ0Mf7O5VrojlPSLWMWR_Gr_qy_ew3rI3Ims18MS8c5E/s1600/BIP20.jpg" height="360" width="640" /></a></div>
<div>
<br /></div>
<div>
The result is working:</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-PUMEFumOVd9JW1kzjB0hOaLmLNGGT1-uRFeGFeLQzZoNKSniCAiiOlpc1VEqr5LXsW4LPvL5ezQD87Twtjq2RZnKecKpPwRJALG8yqBaNCDdr5OcA-QLOaPirqyihDfoSZmR-GorJ5s/s1600/BIP21.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-PUMEFumOVd9JW1kzjB0hOaLmLNGGT1-uRFeGFeLQzZoNKSniCAiiOlpc1VEqr5LXsW4LPvL5ezQD87Twtjq2RZnKecKpPwRJALG8yqBaNCDdr5OcA-QLOaPirqyihDfoSZmR-GorJ5s/s1600/BIP21.jpg" height="368" width="640" /></a></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
Thanks</div>
<div>
<br /></div>
<div>
Until next time</div>
Bojiehttp://www.blogger.com/profile/13163354520521690612noreply@blogger.com0tag:blogger.com,1999:blog-8502142627599800928.post-58924865560715937292014-01-24T15:58:00.000-08:002014-01-24T17:21:20.174-08:00Case Study: Emailing OBIEE dashboards with ad-hoc agent that dynamically passes dashboard prompt values based on user request -- Part 3<a href="http://obinsight.blogspot.com/2014/01/case-study-emailing-obiee-dashboards_23.html" target="_blank">After rpd work is done</a>, let's go to the dashboard.<br />
<br />
Well before that, I am manually creating these users with '_1' and so on in weblogics and make sure they all have their OBIEE account set up with the proper emails.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhbASlhZYCzmYfoNgPq6WY68eggSPahanYHVxhffLJ_dw8_QgCE3N14uJ4PKBCNmkUZ86JIIJM7N8RJDlNLtn0ukB2j5ektXhGI5rXyoDDhyphenhyphenX-fGfq0MJ5q-CJty5eJC5A9c4OFQBNvzwo/s1600/email7.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhbASlhZYCzmYfoNgPq6WY68eggSPahanYHVxhffLJ_dw8_QgCE3N14uJ4PKBCNmkUZ86JIIJM7N8RJDlNLtn0ukB2j5ektXhGI5rXyoDDhyphenhyphenX-fGfq0MJ5q-CJty5eJC5A9c4OFQBNvzwo/s1600/email7.jpg" height="244" width="640" /></a></div>
<br />
Now go to dashboard and we will copy the existing dashboard and reports and create another version for emailing agents.<br />
<br />
First, modify the date and month prompt and manufacturer prompt<br />
<br />
Here in the default section, we are adding session variable as filters:<br />
<br />
The idea is to use timestampadd function here to add a new year and month value based on the integers of the session variable of each 'startmonth' and 'endmonth', then add 'yyyy' with 'mm' to make it 'yyyymm' as numbers. This new value then will be filtered against the year and month column of the date dimension.<br />
If the dashboard is at date level, then for the same logic, we use timestampadd to compute a new date using the integers from session variable 'startmonth' and 'endmonth'.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgxoYhJcNnfU8KJpE-PEBEe-1m1PKbOD2-Hk_jFxsFQA01PW3eVmYk6se_JmarbbF2UriofZXtNlUgejsIYWsHtPqzMxZMGcwFBfE8Rqs_Vhy2Sg6kksCoXU0l4B8GZu7kKZhLsGVbQjKg/s1600/email8.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgxoYhJcNnfU8KJpE-PEBEe-1m1PKbOD2-Hk_jFxsFQA01PW3eVmYk6se_JmarbbF2UriofZXtNlUgejsIYWsHtPqzMxZMGcwFBfE8Rqs_Vhy2Sg6kksCoXU0l4B8GZu7kKZhLsGVbQjKg/s1600/email8.jpg" height="256" width="640" /></a></div>
<br />
<br />
For the other prompts, I realized they are variable prompts, and there is no computation like Date and Month prompt does, so we will use variable expression here to call session variable:<br />
Syntax is below:<br />
@{biserver.variables['nq_session.pay_type']}<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEitamc4o5vLCQdKvGU72280tkD0PKKMkFZg-QWj1y0gzSz2hFlClCGoUKebd9d64RYL-yPUEBUm2GOearoY5VwWst49BOxFtDlkP2qU_d9Yt3QPhHfoL_J6Lkg-_ccUXrAdfcKyEjxleAQ/s1600/email9.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEitamc4o5vLCQdKvGU72280tkD0PKKMkFZg-QWj1y0gzSz2hFlClCGoUKebd9d64RYL-yPUEBUm2GOearoY5VwWst49BOxFtDlkP2qU_d9Yt3QPhHfoL_J6Lkg-_ccUXrAdfcKyEjxleAQ/s1600/email9.jpg" height="264" width="640" /></a></div>
<br />
After getting done with the prompts, we will make necessary changes of the reports and dashboard structure.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiCiKwxSkUkRaSgNRH275CVBaCSoJPt9dBWIUbAHRwgHsJyIEo12U4FKGu8lfSvHfEb1MHKyUYsDRDpK_d8TSCSFGp_0xTEJaPr-OcqtVlfSX_RYZtA5GBVtgpGBVomqfA7jZVD1bIOdt8/s1600/email10.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiCiKwxSkUkRaSgNRH275CVBaCSoJPt9dBWIUbAHRwgHsJyIEo12U4FKGu8lfSvHfEb1MHKyUYsDRDpK_d8TSCSFGp_0xTEJaPr-OcqtVlfSX_RYZtA5GBVtgpGBVomqfA7jZVD1bIOdt8/s1600/email10.jpg" height="200" width="640" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEifSGDwZyzXMJoqrH-RGe_5vDQ1SGj1_UYc8gjXbFo1WKZWBaYA7BFGUml8t5Vp2fDfXhZxZnJKm-KIjEtTYHYai1WgS0OpyyaIgDkOW1K0MvXCJzhxRH6YxLRauvfe6XE8GSC7486vRog/s1600/email11.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEifSGDwZyzXMJoqrH-RGe_5vDQ1SGj1_UYc8gjXbFo1WKZWBaYA7BFGUml8t5Vp2fDfXhZxZnJKm-KIjEtTYHYai1WgS0OpyyaIgDkOW1K0MvXCJzhxRH6YxLRauvfe6XE8GSC7486vRog/s1600/email11.jpg" height="228" width="640" /></a></div>
<br />
Now take a look at the dashboard. Initially the prompt values will mostly be empty because it is run by admin user who doesn't have anything defined in the security table.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfTVvDhejiE6dkV__ZLy89n2N7b6tOZV1WDrM8QmkgQAtSq5GRZL6I3u1VAH5X3FPgToTnylfh3ugcu6LeHa1tVVlCx4KgMeN2qGW2CkLe-Qq7eTns5lS14w4DVsO8-gKdlalZnHfxCok/s1600/email12.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfTVvDhejiE6dkV__ZLy89n2N7b6tOZV1WDrM8QmkgQAtSq5GRZL6I3u1VAH5X3FPgToTnylfh3ugcu6LeHa1tVVlCx4KgMeN2qGW2CkLe-Qq7eTns5lS14w4DVsO8-gKdlalZnHfxCok/s1600/email12.jpg" height="292" width="640" /></a></div>
<br />
<br />
Now create an agent and set it as 'run as recipient':<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhhakto-mijuNejj0pP89bhTiTbH7BnrPQdIaz54UKX3QtUzsJU0ac8IyNsXybXKe-J8d9O_6MWvlImvMlPqNpihZotgxDLaFuC9_S_-4QMAx4MNxKbzcrjfn8cqklEMQFeIfFUkAaG_fc/s1600/email13.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhhakto-mijuNejj0pP89bhTiTbH7BnrPQdIaz54UKX3QtUzsJU0ac8IyNsXybXKe-J8d9O_6MWvlImvMlPqNpihZotgxDLaFuC9_S_-4QMAx4MNxKbzcrjfn8cqklEMQFeIfFUkAaG_fc/s1600/email13.jpg" height="200" width="640" /></a></div>
<br />
Recipients of the agent would be all those test users I created:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTaQIRj2WflgpNUei6E0WiJ51ERq1PYPUB0xlxZYYhvVbUmH5AAKkFmccFzZxpOHndDo16Q2UzR8cxSjv9WRL9HTi3DOcaStgmUwHO6Z39exR-IgTzmL5grITYkWQZsXyLt0yxZj74nww/s1600/email14.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTaQIRj2WflgpNUei6E0WiJ51ERq1PYPUB0xlxZYYhvVbUmH5AAKkFmccFzZxpOHndDo16Q2UzR8cxSjv9WRL9HTi3DOcaStgmUwHO6Z39exR-IgTzmL5grITYkWQZsXyLt0yxZj74nww/s1600/email14.jpg" height="276" width="640" /></a></div>
<br />
Save and manually trigger this agent and see what we got:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZTCgK0pV5oJ77ZbVFoW9XvjyOxHrdJbcFABBvX7MCm4VipW0gkoxJOQT9f4kEdGyKZ3m3cyLPd51M3-hNt4ECuEDfHrpNVM-oZOTPkDyoAAlmHH8rQk7y_93FO7CZcKyhi6HnTg6ZE5M/s1600/email15.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZTCgK0pV5oJ77ZbVFoW9XvjyOxHrdJbcFABBvX7MCm4VipW0gkoxJOQT9f4kEdGyKZ3m3cyLPd51M3-hNt4ECuEDfHrpNVM-oZOTPkDyoAAlmHH8rQk7y_93FO7CZcKyhi6HnTg6ZE5M/s1600/email15.jpg" height="200" width="640" /></a></div>
<br />
Now I got a few emails since all of these users have the same email account, which is mine:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRrWIB-39vrZP5GGvCoqcxn0IDVePVyAOqYV_TuWB9kl7M0wwB0LmJGJzkrKlWi9UcbCoDEltqB7WIUPizcBPClIVK5vJVl8QLDSweUmYjIep24kwhyphenhyphenVgL30_OyWf4uh8QVxz0ROBRfkA/s1600/email16.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRrWIB-39vrZP5GGvCoqcxn0IDVePVyAOqYV_TuWB9kl7M0wwB0LmJGJzkrKlWi9UcbCoDEltqB7WIUPizcBPClIVK5vJVl8QLDSweUmYjIep24kwhyphenhyphenVgL30_OyWf4uh8QVxz0ROBRfkA/s1600/email16.jpg" height="214" width="640" /></a></div>
<br />
Now I have to open each up to make sure the contents are different and the values should be different. The first 3 emails are all for user 'charles', which is recipient charles_1, charles_2,charles_3:<br />
<br />
Ok, 1 for manufacturer 'HMA', customer pay, dealer cost $300 between July 2013 and Sept 2013<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJ1X_wbLKLQ69847_3SQfE3Nzr9Jehj6OZxu_yraWEUxYxmQytnN-nlPGLze_2mqgj-mRCOGWrnXrU-gDSoZ-YRd_bqllo1Y2HS-af8kBE8uQls0K1UGmrRDWewKezGLQsXje1nxkPo7M/s1600/newemail2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJ1X_wbLKLQ69847_3SQfE3Nzr9Jehj6OZxu_yraWEUxYxmQytnN-nlPGLze_2mqgj-mRCOGWrnXrU-gDSoZ-YRd_bqllo1Y2HS-af8kBE8uQls0K1UGmrRDWewKezGLQsXje1nxkPo7M/s1600/newemail2.jpg" height="260" width="640" /></a></div>
<br />
For manufacturer 'AUDIUSA' and different other prompt values:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgajpttoxmBgfLJndT4_6Ee_HYfj7x7KGk1AC8iPqIv2v_Lmuhq3WErNb44nBIUR3OhermqvlIw3coHuehLJqqU_Q6R9B2mPT1k9O9ot7pyk_JmiOyQaQxfA9Q34rQv0sSmfYp2P1GR3oE/s1600/newemail3.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgajpttoxmBgfLJndT4_6Ee_HYfj7x7KGk1AC8iPqIv2v_Lmuhq3WErNb44nBIUR3OhermqvlIw3coHuehLJqqU_Q6R9B2mPT1k9O9ot7pyk_JmiOyQaQxfA9Q34rQv0sSmfYp2P1GR3oE/s1600/newemail3.jpg" height="382" width="640" /></a></div>
<br />
For Manufacturer 'PORSCHEUSA' and different prompt values:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiP9uJh1sRz9yua11pFmNcSd0QXDExwd0OmXOhDNpLS5LMw02XIs_3Y38Cd5ZhQNeSwoIsuaCCC-0T1UifRWTrcDrww9uzPSwkE4rT3J7Tvmx5LHfIC33NDHxl12LoJTkBrokbkUoE6_qo/s1600/newemail4.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiP9uJh1sRz9yua11pFmNcSd0QXDExwd0OmXOhDNpLS5LMw02XIs_3Y38Cd5ZhQNeSwoIsuaCCC-0T1UifRWTrcDrww9uzPSwkE4rT3J7Tvmx5LHfIC33NDHxl12LoJTkBrokbkUoE6_qo/s1600/newemail4.jpg" height="386" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
This report is for user 'dchadsey' and all of the parameters defined for this user in the table are passed along<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgzznPrPs4_7vDDbP9COnSor7uTkmBfAX-jU2LCcmC-VrsyiMHgjTkoauIeL7pWtYEcxD4zaqFzm9SJ6c0Rbau80tnwhXQMkhwFl3ZlS46YCo32GbiMIt7Jgh-UnjJnjIIDG8y9uILj2gk/s1600/newemail1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgzznPrPs4_7vDDbP9COnSor7uTkmBfAX-jU2LCcmC-VrsyiMHgjTkoauIeL7pWtYEcxD4zaqFzm9SJ6c0Rbau80tnwhXQMkhwFl3ZlS46YCo32GbiMIt7Jgh-UnjJnjIIDG8y9uILj2gk/s1600/newemail1.jpg" height="348" width="640" /></a></div>
<br />
Now let's test for error handling. I will manually update dchasey's MFG_HIERARCHY value to 'BMW'. This will be a wrong value for this user because in the security table, this user is not scoped to that manufacturer.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjMxgDqo-N_AuQQOadns3BXUhE91HoCYOolgkwBHl-lkv6qP87QLwZxpqi8q2taQ3viYUXlAqCkgI7Hvy6oz3cC9qmpVvIZw00rQuJI8qAXUB2TJy1e6mcav0E_wVI5SuvDEMgFhyphenhyphenafQZA/s1600/newemail5.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjMxgDqo-N_AuQQOadns3BXUhE91HoCYOolgkwBHl-lkv6qP87QLwZxpqi8q2taQ3viYUXlAqCkgI7Hvy6oz3cC9qmpVvIZw00rQuJI8qAXUB2TJy1e6mcav0E_wVI5SuvDEMgFhyphenhyphenafQZA/s1600/newemail5.jpg" height="156" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Now rerun the agent and i should be getting an email with no data. It would be a breach of confidentiality if this user indeed gets data for BMW that he is not scoped to.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Success!</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhhTnLcYTZ5m0aSThsV8FhQJF_-Pu7HLen9C823qo_UDBtDC-fN2_dYM1EEtkoSIuc489givaQZV8LX5vwmGiU4f0gjFmIubgkH0Q9HbBdg7eUWeK6ZUGwX3D3w3GMgg1W2jn_5uT-x5Dc/s1600/newemail6.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhhTnLcYTZ5m0aSThsV8FhQJF_-Pu7HLen9C823qo_UDBtDC-fN2_dYM1EEtkoSIuc489givaQZV8LX5vwmGiU4f0gjFmIubgkH0Q9HbBdg7eUWeK6ZUGwX3D3w3GMgg1W2jn_5uT-x5Dc/s1600/newemail6.jpg" height="226" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
By changing the value to BMW, the query defined in the session initialization block should return nothing, therefore the 'Manufacturer prompt would be empty.' Of course, I then played some tricks by adding conditions of each dashboard section to hide or display them based on whether manufacturer prompt is blank or not. There are many ways to do that, which I am going to skip for now.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
So, the prove of concept works for a few test users. However, this is still not enough if to implement this at a larger scale. There are two main pieces of the puzzles according to me:</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
1. users will probably document their request in the form of spreadsheet and upload it somewhere. For each request of delivering a different dashboard, there will be a separate table like the one I designed. The user defined parameters will then be loaded into our DB table and adding '_' and sequence number behind each user name will need to be done. Managing this process of communicating between users inputs and DB tables will need to be in places. A lot of decision makings need to happen. User could even request an UI for their updating of request, which will need to be engineered to communicate with the DB Table systematically.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
2. Manually creating of all these user_1s in OBIEE will be a nightmare. I know OBIEE has a project called 'SA SYSTEM Subject area', which once implemented, will automatically populate users into OBIEE and associating it with emails. However, the requirement for this to work is pretty rigid. If I can automatically populate columns for 'user_name' (all of the _1s values) not the actual 'users' column using SA Subject Area, then this would be a huge win.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
3. Different dashboards are created differently. Some dashboards are more complex than others. For each specific dashboard to be delivered, it will have to be modified differently to pass the session variables correctly. There is no one size fits all solution to this approach.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Having said all that, I think I have come to as far as I can come with this solution. It's not perfect solution, in fact, it may be a terrible solution in someone's eyes, but it does work to some extend. Maybe oracle has plans for this type of requirements in their future products.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
For now, I am opened to see other solutions.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Until then </div>
Bojiehttp://www.blogger.com/profile/13163354520521690612noreply@blogger.com2tag:blogger.com,1999:blog-8502142627599800928.post-81178943022813770372014-01-23T15:12:00.000-08:002014-01-24T17:20:52.386-08:00Case Study: Emailing OBIEE dashboards with ad-hoc agent that dynamically passes dashboard prompt values based on user request -- Part 2<a href="http://obinsight.blogspot.com/2014/01/case-study-emailing-obiee-dashboards.html" target="_blank">From the previous time</a>, I have finalized the DB table design, now let's check out what I did in the rpd.<br />
<br />
Below are the session initialization blocks. 'Security' and 'GROUPS' where created for the existing data level and object level security <a href="http://obinsight.blogspot.com/2014/01/case-study-how-to-implement-object_15.html" target="_blank">implemented before.</a><br />
Now I am adding a new one 'Security_Agent':<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5z-U4dx7hEN-NyetiO7XqfdnnUK5cUQLQ2OMmOIG_O6Wf4YWQytiBfFal6C91hFoXOVZxG_Shx55bolmZE3eV6a-MDHEgzjtV1bZ8NwFI-2cvT6LY64Iagf-uY8pQSn1ZC0cV9hqx-Wk/s1600/email3.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5z-U4dx7hEN-NyetiO7XqfdnnUK5cUQLQ2OMmOIG_O6Wf4YWQytiBfFal6C91hFoXOVZxG_Shx55bolmZE3eV6a-MDHEgzjtV1bZ8NwFI-2cvT6LY64Iagf-uY8pQSn1ZC0cV9hqx-Wk/s1600/email3.jpg" height="250" width="640" /></a></div>
<br />
In Security Agent, I have created session variables for each columns of the table. These session variables will be passed on to the dashboard later.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjSO88rjk7zPj8o9pAQcp4R5UImxqHGPdT6ZbYoDp60VbQxQw_CINXRk_RKc7Uj3e7B3N_ioRo6AJAhB5w3buGnIIE7qnexgPTcZ8Ks8zdfzpKqL8-m76Ahv_kut0V8ILej_gI5xqepqRM/s1600/email4.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjSO88rjk7zPj8o9pAQcp4R5UImxqHGPdT6ZbYoDp60VbQxQw_CINXRk_RKc7Uj3e7B3N_ioRo6AJAhB5w3buGnIIE7qnexgPTcZ8Ks8zdfzpKqL8-m76Ahv_kut0V8ILej_gI5xqepqRM/s1600/email4.jpg" height="544" width="640" /></a></div>
<br />
The initialization string is the following query:<br />
<br />
<div style="background-color: white; font-family: arial, sans-serif; font-size: 13px;">
<span style="color: blue;"><b>Select USER_NAME, START_MONTH, END_MONTH, MFG_HIERARCHY, MAKE, PAY_TYPE, AVG_COST,EMAIL, USER_SEQUENCE_NUM, USERS </b></span></div>
<div style="background-color: white; font-family: arial, sans-serif; font-size: 13px;">
<span style="color: blue;"><b>from DW_OBIEE_USER_AUTH</b></span></div>
<div style="background-color: white; font-family: arial, sans-serif; font-size: 13px;">
<span style="color: blue;"><b>where </b></span></div>
<div style="background-color: white; font-family: arial, sans-serif; font-size: 13px;">
<span style="color: blue;"><b>USERS in (select USERNAME </b></span></div>
<div style="background-color: white; font-family: arial, sans-serif; font-size: 13px;">
<span style="color: blue;"><b>from table(Ret_User_Details(substr(<wbr></wbr>':USER', 1, instr(':USER' , '_' , 1) -1 ) )) ) </b></span></div>
<div style="background-color: white; font-family: arial, sans-serif; font-size: 13px;">
<span style="color: blue;"><b>AND USER_NAME = ':USER'</b></span></div>
<div style="background-color: white; font-family: arial, sans-serif; font-size: 13px;">
<span style="color: blue;"><b>and MFG_HIERARCHY in (select company_name </b></span></div>
<div style="background-color: white; font-family: arial, sans-serif; font-size: 13px;">
<span style="color: blue;"><b>from table(Ret_User_Details(substr(<wbr></wbr>':USER', 1, instr(':USER' , '_' , 1) -1 ) )) ) </b></span></div>
<div style="background-color: white; color: #222222; font-family: arial, sans-serif; font-size: 13px;">
UNION</div>
<div style="background-color: white; font-family: arial, sans-serif; font-size: 13px;">
<span style="color: red;"><b>Select USER_NAME, START_MONTH, END_MONTH, MFG_HIERARCHY, MAKE, PAY_TYPE, AVG_COST,EMAIL, USER_SEQUENCE_NUM, USERS </b></span></div>
<div style="background-color: white; font-family: arial, sans-serif; font-size: 13px;">
<span style="color: red;"><b>from DW_OBIEE_USER_AUTH</b></span></div>
<div style="background-color: white; font-family: arial, sans-serif; font-size: 13px;">
<span style="color: red;"><b>where </b></span></div>
<div style="background-color: white; font-family: arial, sans-serif; font-size: 13px;">
<span style="color: red;"><b>USERS in (select USERNAME </b></span></div>
<div style="background-color: white; font-family: arial, sans-serif; font-size: 13px;">
<span style="color: red;"><b>from table(Ret_User_Details(substr(<wbr></wbr>':USER', 1, instr(':USER' , '_' , 1) -1 ) )) ) </b></span></div>
<div style="background-color: white; font-family: arial, sans-serif; font-size: 13px;">
<span style="color: red;"><b>AND USER_NAME = ':USER'</b></span></div>
<div style="background-color: white; font-family: arial, sans-serif; font-size: 13px;">
<span style="color: red;"><b>AND USER_TYPE in (select SCOPE_NAME FROM table(Ret_User_Details(substr(<wbr></wbr>':USER', 1, instr(':USER' , '_' , 1) -1 ) )) )</b></span></div>
<div style="background-color: white; font-family: arial, sans-serif; font-size: 13px;">
<span style="color: red;"><b><br /></b></span></div>
<div style="background-color: white; font-family: arial, sans-serif; font-size: 13px;">
The<span style="color: blue;"> <b>blue</b></span> part of the query takes the input of (:user), which is going to be in the form of 'user_1', strip everything after '_' in order to match in the security user table that the client company already has, then it returns the values of the same user in my new DB table with corresponding dashboard parameters. The user in my table must exist in the security user table, not only that, but the corresponding manufacturer of that user must also match the same in the security table. This takes care of the error handling of man made mistakes. </div>
<div style="background-color: white; font-family: arial, sans-serif; font-size: 13px;">
<br /></div>
<div style="background-color: white; font-family: arial, sans-serif; font-size: 13px;">
The<span style="color: red;"><b> red</b></span> part of the query is identical to the blue part. However, this part is only for users who are company's internal users. If you followed my <a href="http://obinsight.blogspot.com/2014/01/case-study-how-to-implement-object.html" target="_blank">previous implementation of data and row level security</a>, you will realize that for company user, there is no corresponding company name of that user in the security table. It is only flagged by 'HAS_GLOBAL_APP_SCOPE' = 1 in that table. So the blue part of the query takes care of external users, but it will leave out all of the company's internal user, which is not good. Therefore, the blue and the red are both needed and it will be unioned. </div>
<div style="background-color: white; font-family: arial, sans-serif; font-size: 13px;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiIAkKO1wR4OhELpJxt5rJkBpGhx085gupihH30AOlLbhin68FNHPbXOaWI4MsllKfbhCxuG9NGcq_qDKFbN3x4UP470dWtWKw8VbzEdF2-q0IoL-6Z5IqpddIOarJ_ZuQxKsUQ_VE1570/s1600/email5.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiIAkKO1wR4OhELpJxt5rJkBpGhx085gupihH30AOlLbhin68FNHPbXOaWI4MsllKfbhCxuG9NGcq_qDKFbN3x4UP470dWtWKw8VbzEdF2-q0IoL-6Z5IqpddIOarJ_ZuQxKsUQ_VE1570/s1600/email5.jpg" height="544" width="640" /></a></div>
<div style="background-color: white; font-family: arial, sans-serif; font-size: 13px;">
<br /></div>
<div style="background-color: white; font-family: arial, sans-serif; font-size: 13px;">
<br /></div>
<div style="background-color: white; font-family: arial, sans-serif; font-size: 13px;">
All variables are set with default values:</div>
<div style="background-color: white; font-family: arial, sans-serif; font-size: 13px;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEji5nFfzC6yg2hWKvFJBGvJ_iOEhJlTRX-1p9dXC8JbiYwCyhT3NCBj6iXYjqaRKTC_quhMCq3logtKi0_tdWf1WDst08GIbMQQ5AN42Kg9g0tHQw1Ai4EHgfxOGHpkmM66h-wdWIaVlgg/s1600/email6.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEji5nFfzC6yg2hWKvFJBGvJ_iOEhJlTRX-1p9dXC8JbiYwCyhT3NCBj6iXYjqaRKTC_quhMCq3logtKi0_tdWf1WDst08GIbMQQ5AN42Kg9g0tHQw1Ai4EHgfxOGHpkmM66h-wdWIaVlgg/s1600/email6.jpg" height="336" width="640" /></a></div>
<div style="background-color: white; font-family: arial, sans-serif; font-size: 13px;">
<br /></div>
<div style="background-color: white; font-family: arial, sans-serif; font-size: 13px;">
<br /></div>
<div style="background-color: white; font-family: arial, sans-serif; font-size: 13px;">
This is all I need for the rpd.</div>
<div style="background-color: white; font-family: arial, sans-serif; font-size: 13px;">
<br /></div>
<div style="background-color: white; font-family: arial, sans-serif; font-size: 13px;">
<a href="http://obinsight.blogspot.com/2014/01/case-study-emailing-obiee-dashboards_24.html" target="_blank">Next time</a>, the most tedious work will be begin, which is modifying dashboards to receive these session variables.</div>
<div style="background-color: white; font-family: arial, sans-serif; font-size: 13px;">
<br /></div>
<div style="background-color: white; font-family: arial, sans-serif; font-size: 13px;">
Stay tuned.</div>
<div style="background-color: white; font-family: arial, sans-serif; font-size: 13px;">
<br /></div>
<div style="background-color: white; font-family: arial, sans-serif; font-size: 13px;">
Thanks</div>
Bojiehttp://www.blogger.com/profile/13163354520521690612noreply@blogger.com0tag:blogger.com,1999:blog-8502142627599800928.post-49273843206967482602014-01-22T14:52:00.000-08:002014-01-23T15:19:37.847-08:00Case Study: Emailing OBIEE dashboards with ad-hoc agent that dynamically passes dashboard prompt values based on user request -- Part 1Hello<br />
<br />
Today I want to share this requirement that I encountered. This seems like a straightforward implementation but it's tricky actually. Currently, OBIEE's agent framework is not flexible enough to achieve this, therefore I feel like sharing this requirement out and maybe someone else will have even better ideas. First, let me explain the requirements and the background.<br />
<br />
Take the below dashboard for instance:<br />
The dashboard has 5 prompts and we all know what they are for.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTIcD0EnnE6ZIoflc_o1nONbitwWhwv5mIeaHNjWAnlhISDTAZSi821EjtL6RWN1P3MX48V99rIxkcUdbS4gzSE6CR2-liNu3N2FLvsmB3n2f-nbMNJKRBgFu8y95nEhpQ3sIFE42u5fI/s1600/email1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTIcD0EnnE6ZIoflc_o1nONbitwWhwv5mIeaHNjWAnlhISDTAZSi821EjtL6RWN1P3MX48V99rIxkcUdbS4gzSE6CR2-liNu3N2FLvsmB3n2f-nbMNJKRBgFu8y95nEhpQ3sIFE42u5fI/s1600/email1.jpg" height="284" width="640" /></a></div>
<br />
Now, because of <a href="http://obinsight.blogspot.com/2014/01/case-study-how-to-implement-object_16.html" target="_blank">the security framework we have already implemented</a> that restrict user access at data level based on their groups, not every user will be looking at the same dashboard contents. Therefore, when this report is delivered to each user with emails, user wants to see the reports based on their particular interest. In other words, they want the agent to have some sort of ad-hoc feature that allows them to define their particular prompt values and receive reports based on that.<br />
<br />
To illustrate with an example, user A wants to see this dashboard delivered to him for data that are only 6 months old, for manufacturer BMW only and Payment Type is warranty, and average cost is $500. User B wants to get the same dashboard but with data that is only for 3 months old, and manufacturer is only Audi, Payment type is Customer Pay and average cost is $1000. Now an user could be servicing multiple car manufacturers at the same time, then the same user may ask for multiple versions of the same dashboards to be delivered separately. He may want one for Volkswagen of 2 months and another one for Toyota of 4 months delivered with separate emails so he can work with those manufacturers individually. Also, the system should be complied with the same data level security that if the user by mistake request the wrong manufacturer that he is not scoped to, he should get an email with empty dashboard. The confidentiality of the data should be protected and the implementation should take into account of any man made mistakes.<br />
<br />
I know OBIEE doesn't have any existing features that allows ad-hoc query of agent framework, because ad-hoc is available only when you are logged on to the system to run dashboards yourself. If this requirement were to be implemented, we may end up creating different versions of dashboard for different user's request. This can become a nightmare. Now other open source reporting tools, like Jasper Report can implement these kind of requirements with some loop back functions, because they are open source, it provides greater flexibility in terms of features, but on the other hand, the technical requirement of the developer is higher too. In other words, you have to be better at programming to make Jasper a much more powerful tool than OBIEE. Think in terms of Android vs IOS. I digressed.<br />
<br />
I still decided to give this a crack in OBIEE just to see how far I can go. The first thing that came to my mind was to find a way to pass these prompt values to the dashboard whenever an agent runs the query. These values should be different based on the user name. Sounds like a job of session variables. But first and foremost, these values will be provided from users as request, we need to first design a way to store these information. Therefore, I have decided to create a table for that.<br />
<br />
A table like the below was created, and after numerous trial and errors I have come to this structure<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgsNqa5Cay-LB18QkOc88hBXALtWTuPNppiq_vCOBN0DtZqZzQntwH3knJbYEDr2SJzSvrbN27MfZCZVFExTyxiSjeTqYa9HIxWZYZAEF_9RT6V-KvDB0gEiUCZWYTJ1Ct1CCJugZkzwCY/s1600/email2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgsNqa5Cay-LB18QkOc88hBXALtWTuPNppiq_vCOBN0DtZqZzQntwH3knJbYEDr2SJzSvrbN27MfZCZVFExTyxiSjeTqYa9HIxWZYZAEF_9RT6V-KvDB0gEiUCZWYTJ1Ct1CCJugZkzwCY/s1600/email2.jpg" height="192" width="640" /></a></div>
<br />
So it has the following attributes:<br />
<br />
Users: This column stores the real user name of the client company. The users here must match the users in the security table that I <a href="http://obinsight.blogspot.com/2014/01/case-study-how-to-implement-object.html" target="_blank">explained for another project </a> in order to comply with the data level and object level security<br />
<br />
User_Name: This column is nothing but users with '_' and sequence numbers. I have 3 rows for user 'charles' because this user is asking for 3 different versions of the same dashboard. So this column will be charles_1, charles_2, charles_3. If user only request for 1 version, then there is only 1 row for that user in this table and the value of this column will be users_1 (dchadsey_1). This is the unique column.<br />
<br />
Startmonth, endmonth: These are the parameters that will be passed to the dashboard's date or month prompts. These values will be integers, '-' means how many months or days back.<br />
<br />
MFG_HIERARCHY, MAKE, PAY_TYPE, AVG_COST: These are other attributes users will define which will be passed to the dashboard prompt.<br />
<br />
Email: User's email address.<br />
<br />
User_type: This tells you whether this user is company user or external users.<br />
<br />
The reason I have come to this design is the conclusion after numerous trials and errors:<br />
<br />
I tried a different approach before, which is to not have user_name column. Use users and create session variables with row-wise initialization hoping that the agent will query the this table row by row for the same user. But it doesn't work this way. On my lucky day (I don't even remember what I did), I was only getting 1 email with the first row of the table being passed. The other 2 requests from this user were not run. On a normal day (I tend to think I am not normal for tackling this task), the query with row-wise initialization will not return any data. <br />
<br />
Therefore, I have decided to fool OBIEE into thinking that the requests are not coming from the same user 'charles', but rather, from 3 different users: charles_1, charles_2, charles_3. Once these 3 recipients are added to the agent with the same email address, all 3 requests from 'charles' will be fulfilled.<br />
<br />
Enough of today. <a href="http://obinsight.blogspot.com/2014/01/case-study-emailing-obiee-dashboards_23.html" target="_blank">In part 2</a>, I will explain how I configure the rpd and modify dashboards for this to work.<br />
<br />
Stay tuned.<br />
<br />
<br />Bojiehttp://www.blogger.com/profile/13163354520521690612noreply@blogger.com0tag:blogger.com,1999:blog-8502142627599800928.post-30170289339686504782014-01-16T21:28:00.000-08:002014-01-16T22:32:16.685-08:00Case study: How to implement Object level security and Data Level Security in OBIEE 11G - Part3<a href="http://obinsight.blogspot.com/2014/01/case-study-how-to-implement-object_15.html">Last part </a>we have gone through the DB changes and RPD configuration for implementing data level and object level securities. Now there are some final pieces that needs to be done in order to make this implementation perfect. Let's start with object level security. We need to set them up so that different user group will only have access to their dashboards first.<br />
<br />
So here are the different folders that stores dashboards and reports meant for different users.<br />
<br />
As you can see, I already know what kind of access each folder should have as shown below:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7_uAYb0LZ_pS01zWDPxGm2-aNQO-feWXQX3jgEWFoZCfiEsazwZ3rIM3Nf4SIbVN51uW0I-CRlNu7HzWJP1doFoPgJEit-ClMicBXEyvZu4gR-6pYSyir7E3-b2RMwzXlbr12motn03Y/s1600/Se24.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7_uAYb0LZ_pS01zWDPxGm2-aNQO-feWXQX3jgEWFoZCfiEsazwZ3rIM3Nf4SIbVN51uW0I-CRlNu7HzWJP1doFoPgJEit-ClMicBXEyvZu4gR-6pYSyir7E3-b2RMwzXlbr12motn03Y/s640/Se24.jpg" height="324" width="640" /></a></div>
<br />
I will show you what I did for manufacturer and dealer group folders. Each will be assigned to the proper application roles with the right level of privileges:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg2QairvkhiAD1rSrhUmTSM1kWuh_NJRAHy5ip2yQiZfEtQMFSieQcsAn8Sno8K8LfRzxBa4L26L5GKDdfUND9z3myP97s1DDP7f-2Gm6jQFiYDJ0C3ayLFLmkiSiu6H-0T3zixyJvguHA/s1600/Se25.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg2QairvkhiAD1rSrhUmTSM1kWuh_NJRAHy5ip2yQiZfEtQMFSieQcsAn8Sno8K8LfRzxBa4L26L5GKDdfUND9z3myP97s1DDP7f-2Gm6jQFiYDJ0C3ayLFLmkiSiu6H-0T3zixyJvguHA/s640/Se25.jpg" height="348" width="640" /></a></div>
<br />
Same idea for partner and dealer folders, which I am going to skip now. For internal user, the below is the configuration:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjW1dbVfT3DuIQBPhRRT8cfrAxflfwvkzRWOmBGYMrxIJVSXeqvipD8h4J6UK_CHl63bNn4hSj0RwKlmAPeDk_52HhE1wXwPQxKVyAEdE80gwmvP6r6MbLuZ8sgScyZvCFlkF_rBuETI4g/s1600/Se26.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjW1dbVfT3DuIQBPhRRT8cfrAxflfwvkzRWOmBGYMrxIJVSXeqvipD8h4J6UK_CHl63bNn4hSj0RwKlmAPeDk_52HhE1wXwPQxKVyAEdE80gwmvP6r6MbLuZ8sgScyZvCFlkF_rBuETI4g/s640/Se26.jpg" height="320" width="640" /></a></div>
<br />
There are other dashboard objects that allows permission setting, this will further controls object level access based on user groups. I am going to skip all these steps now.<br />
<br />
For testing, let's see how things are working when these different users log in:<br />
<br />
1. Manufacturer user logs in:<br />
Object level Security working:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEigICvjHIxp50_JFCK87A9gLHoEcQM_PG5yI1V50fIKP6eOZkymIwmaRCybLDAsr7zFerIf834D9yNEwc69TyYHHwoM7fb1ti-ssRKecC-Pa_NwWvtQ0ipfozwQsJ83IQiZOOxWYzcbaLw/s1600/Se1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEigICvjHIxp50_JFCK87A9gLHoEcQM_PG5yI1V50fIKP6eOZkymIwmaRCybLDAsr7zFerIf834D9yNEwc69TyYHHwoM7fb1ti-ssRKecC-Pa_NwWvtQ0ipfozwQsJ83IQiZOOxWYzcbaLw/s1600/Se1.jpg" height="150" width="640" /></a></div>
<br />
Data level security working:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgrJ3LQtdeqDStYawnPygkItA6syAL4RMDi1kOLtw6R1KTRSBi58OZmPRQ-D6DXGMnIx-EyFbWuaIKA87HsMVpeDqrFuErf1RFABwnM4fSxkdhR-YjCiy1IvjYcQum7R4WXvk1_3IQYdWA/s1600/Se2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgrJ3LQtdeqDStYawnPygkItA6syAL4RMDi1kOLtw6R1KTRSBi58OZmPRQ-D6DXGMnIx-EyFbWuaIKA87HsMVpeDqrFuErf1RFABwnM4fSxkdhR-YjCiy1IvjYcQum7R4WXvk1_3IQYdWA/s1600/Se2.jpg" height="238" width="640" /></a></div>
<br />
2. Dealer Group user logs in:<br />
<br />
Object level security working:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh8v2Z96kssLCIF88W3JODWHoCQgCp_i3dAHJhSUo0HD7DjnZRdniouPFwh_hB0VKue5Z7WMd3zQJFozLcsyrrG1j8bjVsrF4wD8-M6Dm5YDvsIhiKEQaM8YubQET5kJRPNnaZBTshuogw/s1600/Se3.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh8v2Z96kssLCIF88W3JODWHoCQgCp_i3dAHJhSUo0HD7DjnZRdniouPFwh_hB0VKue5Z7WMd3zQJFozLcsyrrG1j8bjVsrF4wD8-M6Dm5YDvsIhiKEQaM8YubQET5kJRPNnaZBTshuogw/s1600/Se3.jpg" height="144" width="640" /></a></div>
<br />
Data level security working:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgu6O7Bbwqz8zzt3U2L7yzx8INNDbn-__Rd04SqQVO5Ic1Tjkd_-4bzFV6IV5HYCD03F-_0RhHRGn6Ul8gH2E7afM1rqRicesGRYETcn-q6-v5JgXMOtoTasXLj4X75nrOFJZ7ySdUYGi4/s1600/Se4.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgu6O7Bbwqz8zzt3U2L7yzx8INNDbn-__Rd04SqQVO5Ic1Tjkd_-4bzFV6IV5HYCD03F-_0RhHRGn6Ul8gH2E7afM1rqRicesGRYETcn-q6-v5JgXMOtoTasXLj4X75nrOFJZ7ySdUYGi4/s1600/Se4.jpg" height="346" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
3. Other user groups are working the same way, I am gonna skip posting the screenshots. </div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Let's look at company internal user logging in:</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
For object level security, this user is unrestricted so all dashboards are accessible:</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjkYn5q8ATtIosuUVPWpDPLMcEEP20nRFbtdMPqCdSmb0EVZHtbdk2xiSF3FB4h32JlrXRw3Cf7wEMNfYu0oobjdqqJKZtfDslAOS_3LA7t4ijq_naoQW7ooeylx9DUTI8ClcDiGig5Bmw/s1600/Se9.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjkYn5q8ATtIosuUVPWpDPLMcEEP20nRFbtdMPqCdSmb0EVZHtbdk2xiSF3FB4h32JlrXRw3Cf7wEMNfYu0oobjdqqJKZtfDslAOS_3LA7t4ijq_naoQW7ooeylx9DUTI8ClcDiGig5Bmw/s1600/Se9.jpg" height="290" width="640" /></a></div>
<br />
For data level security, this user is unrestricted, so he can see all data:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgvZL1MyXhOtKplEbssD_4MwkvTibVY3-WCJtYM3Poar-jnsLAbx5TkBF5c6mF7pvlD64v17x6HV8kkDaBYa4OhUGp-xAV8PiMrzrgevhvkI8KKKQ2tw2M77nKgDqC7_qjyKalTzrV2FPI/s1600/Se10.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgvZL1MyXhOtKplEbssD_4MwkvTibVY3-WCJtYM3Poar-jnsLAbx5TkBF5c6mF7pvlD64v17x6HV8kkDaBYa4OhUGp-xAV8PiMrzrgevhvkI8KKKQ2tw2M77nKgDqC7_qjyKalTzrV2FPI/s1600/Se10.jpg" height="262" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Thanks </div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Until next time</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<br />
<br />Bojiehttp://www.blogger.com/profile/13163354520521690612noreply@blogger.com1tag:blogger.com,1999:blog-8502142627599800928.post-23141862025112384192014-01-15T23:26:00.000-08:002014-01-16T22:31:52.176-08:00Case study: How to implement Object level security and Data Level Security in OBIEE 11G - Part2<a href="http://obinsight.blogspot.com/2014/01/case-study-how-to-implement-object.html">Last time</a>, we have described the security requirements and the basic implementation strategy. Now let's take a look at the next step after the user table has been created.<br />
<br />
One thing I forgot to mentioned from the last entry was that, the password has encryption. Therefore, a method of decoding encrypted password is needed. Having that into consideration, we have created a few DB functions.<br />
<br />
1. Ret_User_Auth does the authentication process by passing user input logins and matches with the DB table to check for authentication.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgf3O9Qv1sLVOiZJ4tAQCnO3eilA9XhIsKUeuOEJMjxlG63yP6hDYi5URV764GOPsqealjAhAnF3RklphocTuf87_0zS2rhE6tx70Ej_zkCTJE4uxhpg5NJyDgHp4ZVJxeYAEA9s4DGMjU/s1600/Se12.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgf3O9Qv1sLVOiZJ4tAQCnO3eilA9XhIsKUeuOEJMjxlG63yP6hDYi5URV764GOPsqealjAhAnF3RklphocTuf87_0zS2rhE6tx70Ej_zkCTJE4uxhpg5NJyDgHp4ZVJxeYAEA9s4DGMjU/s640/Se12.jpg" height="418" width="640" /></a></div>
<br />
<br />
2. Ret_User_Details takes the same logins passed from Ret_User_Auth and finds it's corresponding groups and scopes.<br />
Below test case shows you the scope name (colonial VW) and group name (dealer group) of test user u1<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYe_Xh8-yuB5kGco895j_8C7vbjkEbKFSBw39Iz2ERL8z7e-YpOgYZBncxLdqsZc04ETR9qVKr1gMoMcS-ZTtSIbIjE_Wjn85PPkuJm_q3g8Sf6IWDXhc6dvg7nQQiRtvGtTAEF6fyAxg/s1600/Se11.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYe_Xh8-yuB5kGco895j_8C7vbjkEbKFSBw39Iz2ERL8z7e-YpOgYZBncxLdqsZc04ETR9qVKr1gMoMcS-ZTtSIbIjE_Wjn85PPkuJm_q3g8Sf6IWDXhc6dvg7nQQiRtvGtTAEF6fyAxg/s640/Se11.jpg" height="258" width="640" /></a></div>
<br />
Function Ret_User_Auth actually calls another function which does encryption. So typically, the encryption function is another object that is created for this implementation. I am not going to show you the details of these 3 functions, you can contact me privately if you want to see the sample.<br />
<br />
Now that these are done, let's go to OBIEE's admin tool and create the following session variable initialization blocks:<br />
<br />
CompanySecurity: executes Ret_User_Auth<br />
GROUPS: executes: Ret_User_Detail<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjkStJwM0_CyqyUk75X4iLuTwIsdAvUbYFenFsDr03Hgfbd3FDURyUD590x8fAbeySs0UBUSEEy-Lc3ULmwgnBuy8KLVpEEkH6UG4BVyD0YVtLUy9HqXd4C9bzkVLB12a-MixpJ_8SCG6s/s1600/Se13.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjkStJwM0_CyqyUk75X4iLuTwIsdAvUbYFenFsDr03Hgfbd3FDURyUD590x8fAbeySs0UBUSEEy-Lc3ULmwgnBuy8KLVpEEkH6UG4BVyD0YVtLUy9HqXd4C9bzkVLB12a-MixpJ_8SCG6s/s640/Se13.jpg" height="244" width="640" /></a></div>
<br />
Below are the contents of these 2 initialization blocks:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgtvM6dwmxzlxqW0ZtGgQbWaEgA-vqkhzSSd6FJCuJMM3LoLGOg6cVxECek6YlDa3gZ9E3qBnW_gqEDhakA9VuXbc0gU_pKyTcUaxEBlm_cIvxStJ0zd_muEkjRRZjnLiq9UCEkMBKoM64/s1600/Se14.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgtvM6dwmxzlxqW0ZtGgQbWaEgA-vqkhzSSd6FJCuJMM3LoLGOg6cVxECek6YlDa3gZ9E3qBnW_gqEDhakA9VuXbc0gU_pKyTcUaxEBlm_cIvxStJ0zd_muEkjRRZjnLiq9UCEkMBKoM64/s640/Se14.jpg" height="332" width="640" /></a></div>
<br />
<br />
Group initialization block returns 4 variables: Username, Groups, Scope_names and Dealer_Names. It also has precedence of CompanySecurity initialization block, this means that CompanySecurity initialization block will always be executed before.<br />
<br />
Scope_name will return you the name of manufacturer or dealer group or partner that this user is scoped to. So for the above example of user u1, if he logs in to OBIEE, the value of scope_name variable will be 'colonial VW'. This is why session variable is used for user authentication, not dynamic variable, because when u1 logs in, the scope_name variable will be populated as 'colonial VW' just for this user session, when a different user logs in, he will have a different session which will populate scope_name with a different value that's associated to that user in the table. Dynamic variable doesn't do that. It doesn't differ based on sessions.<br />
<br />
Anyways, enough said. Now that both initialization blocks and variables are created using these 2 functions, let's do the next step, which is important for achieving data level security. We need to apply data level filters for each user groups, which in terms of Admin Tool, it's the application roles.<br />
<br />
So look at the below screenshot:<br />
<br />
We have 6 application roles corresponding to 6 different user groups & scopes.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_NWnRfDMlqUKzsC2j_pwC-6-LWMO2io2k0qO1jOh94OVHAEHQderM7HILpJD4iLOpfS6DBHUFrOW6a1pdkSQ1Mpx8WE9Fk582UuPxxL3vxEerpZNYoB25hL15ucAJPPDEBl3KwJyIfpo/s1600/Se15.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_NWnRfDMlqUKzsC2j_pwC-6-LWMO2io2k0qO1jOh94OVHAEHQderM7HILpJD4iLOpfS6DBHUFrOW6a1pdkSQ1Mpx8WE9Fk582UuPxxL3vxEerpZNYoB25hL15ucAJPPDEBl3KwJyIfpo/s640/Se15.jpg" height="222" width="640" /></a></div>
<br />
They obviously have been created through weblogic, just and FYI:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdfUsJF4bmRA3TkgH_Uaov9A5qwjxOWjD7gocTWzgGxMKEL_hpUmiUPInLEmRKAWzzwldpShdD5VpU3CyGioXc4BPQGon1wfbKvd5yDISGPJtaBAvUZ5QRQk999S5mYLExoICFqSemSXc/s1600/Se16.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdfUsJF4bmRA3TkgH_Uaov9A5qwjxOWjD7gocTWzgGxMKEL_hpUmiUPInLEmRKAWzzwldpShdD5VpU3CyGioXc4BPQGon1wfbKvd5yDISGPJtaBAvUZ5QRQk999S5mYLExoICFqSemSXc/s640/Se16.jpg" height="336" width="640" /></a></div>
<br />
Now for each application roles, depending on their scoping requirement, we need to set the filter for it's permission specifically.<br />
<br />
For manufacturer group, it needs to be applied to all of the columns under manufacture dimension where the manufacturer name = session variable 'scope_name'<br />
Remember, scope_name in my case returns the actual manufacturer, partner, dealer group name that the report should filter on.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDXxNuchYtEKVWlZVL5Kmn5NlN8oDFcw-ihRU4R4RWyJaXtWzcVhNXZ2icgLZYQwordhSB61aChA7gTtMKM-6j79N3T01tjOH4uID1HNPYi4VvfN99jgWlsuXKSVlAnUfvAfzfdGoH_94/s1600/Se17.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDXxNuchYtEKVWlZVL5Kmn5NlN8oDFcw-ihRU4R4RWyJaXtWzcVhNXZ2icgLZYQwordhSB61aChA7gTtMKM-6j79N3T01tjOH4uID1HNPYi4VvfN99jgWlsuXKSVlAnUfvAfzfdGoH_94/s640/Se17.jpg" height="348" width="640" /></a></div>
<br />
For Dealer group scope, it is similar setting on dealer group dimension:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgNUKblVk5c7gqYa5ATToorVzmMDM3vvprK-YjcKelv7hiD0vIK3YUvksENHDfqrRBi8saIi56LsJvCbh-3Wi0Df1A8Ta9LDLWlUByLhfNKq6_ZZwCebws8Wc2sxFs-PvRlWwgzCmM3ums/s1600/Se18.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgNUKblVk5c7gqYa5ATToorVzmMDM3vvprK-YjcKelv7hiD0vIK3YUvksENHDfqrRBi8saIi56LsJvCbh-3Wi0Df1A8Ta9LDLWlUByLhfNKq6_ZZwCebws8Wc2sxFs-PvRlWwgzCmM3ums/s640/Se18.jpg" height="348" width="640" /></a></div>
<br />
For dealer user, since the requirement is that they can access both manufacturer and dealer dashboard filtered for the specific dealer, they will have 2 filter conditions applied, each one is for manufacturer and dealer dimension separately.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPI0FYxunZ7ZbtwMake8tCYN1DE4Hch9493lRoaul_oobSp1JTeqtiXSxPp9cWu7fLhM876w_cnyAXeX65Rccbf0BvzIacO1IqYYpP3AzgR0M0nx8xeLbd_7MROYoLp3B-vhLLIVzVsSk/s1600/Se21.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPI0FYxunZ7ZbtwMake8tCYN1DE4Hch9493lRoaul_oobSp1JTeqtiXSxPp9cWu7fLhM876w_cnyAXeX65Rccbf0BvzIacO1IqYYpP3AzgR0M0nx8xeLbd_7MROYoLp3B-vhLLIVzVsSk/s640/Se21.jpg" height="296" width="640" /></a></div>
<br />
<br />
Now for the internal users, they are called 'staff' by the application role. Since they should have access to all of the contents everywhere, their condition is simply left blank:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhfH1PO5LAtah_ESmmgyT9gkaFMP1r7vKnzk1TMJ4_1PT3o_jHnu2ULq1DtMrSj8xYyqZKxPNTT2uYXmRcUz-NcMzpBvWmx7H67H3-g_N5YOiC0756cQwIaVzkoiyI7iFFI9QHGoyFJzg0/s1600/Se23.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhfH1PO5LAtah_ESmmgyT9gkaFMP1r7vKnzk1TMJ4_1PT3o_jHnu2ULq1DtMrSj8xYyqZKxPNTT2uYXmRcUz-NcMzpBvWmx7H67H3-g_N5YOiC0756cQwIaVzkoiyI7iFFI9QHGoyFJzg0/s640/Se23.jpg" height="324" width="640" /></a></div>
<br />
Now you should have a basic idea of how our data model is like. It's a pretty straightforward star-schema implementation where different user scopes are separated by it's own dimension<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiXDTcNrZRLadSY9ZJiHU67S4AZ966GQwIxMpoKqi1ybU7D4fXFGe2RORKy_UsIwg_n9y5aVsx1OM7q_xLO8NPGOk7GAOQtaV43NPbtuksG2O3iMYPKSApO1W2RL6CFXBJH0_kqygA49pw/s1600/Se28.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiXDTcNrZRLadSY9ZJiHU67S4AZ966GQwIxMpoKqi1ybU7D4fXFGe2RORKy_UsIwg_n9y5aVsx1OM7q_xLO8NPGOk7GAOQtaV43NPbtuksG2O3iMYPKSApO1W2RL6CFXBJH0_kqygA49pw/s640/Se28.jpg" height="278" width="640" /></a></div>
So the idea is that, when a user logs in, the session variable scope_name will be initialized with a specific value, it could be 'colonial VW' if this is a dealer group user, it would be 'General Motors' if this is a manufacturer user, it could be blank if it's an internal user. So for each dimension, this filter is applied at the rpd level so that when this user access to a dashboard (by having the right object level security to begin with), he can only view reports where the manufacturer name (or dealer group name or partner) = 'general motor' (or 'colonial VW' or a partner scope name). If this user is internal user, then scope_name is blank, he will see everything unrestricted.<br />
<br />
Now the last step will be to apply object level security and to apply these session variables at the proper place on answers and dashboards,which we will cover <a href="http://obinsight.blogspot.com/2014/01/case-study-how-to-implement-object_16.html">in the next part</a>.<br />
<br />
Stay tuned<br />
<br />Bojiehttp://www.blogger.com/profile/13163354520521690612noreply@blogger.com1tag:blogger.com,1999:blog-8502142627599800928.post-91965811527070352492014-01-15T22:44:00.000-08:002014-01-16T05:19:16.124-08:00Case study: How to implement Object level security and Data Level Security in OBIEE 11G - Part1Hello Again.<br />
<div>
<br /></div>
<div>
Recently my team has implemented data level security and object level security in OBIEE 11G for a client who is selling their data to external customers. The requirement is very quintessential of any BI Project and the implementation process is trivial. Therefore, I have decided to detail the process, which I think can be very beneficial to any future security implementation.</div>
<div>
<br /></div>
<div>
A little bit of background here. This is a client that incorporates OBIEE into their software solutions to external customers. They are making their BI Platform available through the integration with other apps, which the external customers can subscribes to with a service fee and view the data pertaining to themselves specifically. Since most of the external customers are individual auto dealers and manufacturers, the sheer number of consumers is big and the confidentiality of their data is essential. The requirement pretty much comes down to the following:</div>
<div>
<br /></div>
<div>
1. Individual dealers can log on to the apps and view reports and dashboard for only that dealer</div>
<div>
<br /></div>
<div>
2. If an account is associated with 2 dealers, then it's called 'dealer set'. They can only view dealer set dashboards and reports that the content must be pertaining to that specific dealer set</div>
<div>
<br /></div>
<div>
3. An account can be associated with dealers as well as the manufacturers, such as Honda, BMW etc. Then this is a manufacturer account, so manufacturer dashboard and reports can be accessed for only the data of that manufacturer. </div>
<div>
<br /></div>
<div>
4. At the parallel level of manufacturer are auto partners and auto dealer group accounts. There will be separate dashboards available for partner and dealer group, each is exclusive from the rest. Only the specific dealer group or partner data will be available for the specific account associated with it.</div>
<div>
<br /></div>
<div>
5. There are also users from within the company itself. They are typically sales people. They should be able to access all of the reports & dashboards and see data for all of the dealers, manufacturers, dealer groups at will.</div>
<div>
<br /></div>
<div>
This requirement can be simply translated to users & user group where users will be individual account and user group will be the specific scope of that user, such as dealer, dealer group, partners, manufacturer or company's internal users. So let's think about how to implement this requirement. </div>
<div>
<br /></div>
<div>
First and foremost, before implementing data and object level security, we need to first decide the process of authenticating these users into OBIEE. For the most obvious reason, that most of the users will be be stored in LDAP server since they are external to the company, they will be managed by a table. Therefore, external table authentication becomes the choice. Now the first thing to do is designing the table structure that will accommodate all these different users and scopes.</div>
<div>
<br /></div>
<div>
Below is the table designed for storing all of the users and associated dealers and scopes:</div>
<div>
I have created 6 users for six different scope:</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhk-TL-eBLZ7SqTvGRR1omD-YbsIDg7VIHX4jb8OgA2Ws6aWRzPJkNziLMcAnUMIaVnsYlH-0Z7ZQyEkbDtHMpw-f7SbuKrlcnX23F4zrwmI4qPkpGqgiIDD1AXB2_d-7PBYK9NUKFWp0E/s1600/Se0.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhk-TL-eBLZ7SqTvGRR1omD-YbsIDg7VIHX4jb8OgA2Ws6aWRzPJkNziLMcAnUMIaVnsYlH-0Z7ZQyEkbDtHMpw-f7SbuKrlcnX23F4zrwmI4qPkpGqgiIDD1AXB2_d-7PBYK9NUKFWp0E/s640/Se0.jpg" height="432" width="640" /></a></div>
<div>
<br /></div>
<div>
Company user is flagged bu HAS_GLOBAL_APP_SCOPE = 1. That way, each company user will only occupy one row in the table and by default, we will not restrict such user from accessing any OBIEE report both at object level and data level. Their corresponding company and dealer name will be left blank because they have access to all by default.</div>
<div>
<br /></div>
<div>
Dealer Group users will have the company_type = 'dealer group' and dealer_name = all of the dealers under that dealer group (Colonial VW in my test case).</div>
<div>
<br /></div>
<div>
Partner and Manufacturer users will be similar to dealer group users, except the company_type will change accordingly. </div>
<div>
<br /></div>
<div>
Dealer Set user will have 2 dealer, indicating it's a set. Their company_name will be populated with a dealer set name (Sorry i forgot to populate that from the above screenshot).</div>
<div>
<br /></div>
<div>
Dealer user will have only 1 record and the company_name will be left blank.</div>
<div>
<br /></div>
<div>
An overview of the user & user group hierarchy will look more or less like the following:</div>
<div>
<br /></div>
<div>
I have left out the internal company user from the diagram because they are unrestricted. Dealer is pretty much the same as user since 1 user for only 1 dealer association is considered 'dealer user' anyways.</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg6N42aYqMmesCphr-8tWLmpvlsxWfVCXL7sR5tTA39Qq9FUpc5g1nct7CfR_S0IHYApzgEuE-1SFSR_RKF3d-DpPV2OvYgrBDEvEmhSNBIxoYFU502LTDyhbWeEhBEk2ziXTCusK5HXIw/s1600/Se27.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg6N42aYqMmesCphr-8tWLmpvlsxWfVCXL7sR5tTA39Qq9FUpc5g1nct7CfR_S0IHYApzgEuE-1SFSR_RKF3d-DpPV2OvYgrBDEvEmhSNBIxoYFU502LTDyhbWeEhBEk2ziXTCusK5HXIw/s640/Se27.jpg" height="294" width="640" /></a></div>
<div>
<br /></div>
<div>
Now having done that, we need to start planning the strategy of authenticating users again the information we have stored. </div>
<div>
<br /></div>
<div>
In any case where you have user and associated groups with different accessing ability, the standard process of implementing external table authentication and then assigning authorizing roles can be broken down into 2 steps:</div>
<div>
<br /></div>
<div>
1. User will attend to logon to the application by passing their username and password that they type onto the screen. These 2 parameters should be passed onto the user table to see if this username/password combination exists or not in the table. If it does, it will return username and password as output and user will get authenticated so their logging in is successful. This step can be named 'authentication'.</div>
<div>
<br /></div>
<div>
2. The output of the above step will be username and password. These 2 values will then need to be associated with the corresponding scope or user group. This step, let's call it 'Authorization' or 'scoping' or whatever you want for your own memory.</div>
<div>
<br /></div>
<div>
Now in <a href="http://obinsight.blogspot.com/2012/10/basic-external-table-authentication.html">my other project</a>, I have done the above 2 steps using different table structures from this. I had 3 tables, one for user only, one for user group only and one stores user & group relationships. But here, we are storing everything in just 1 table. The difference means that we will configure OBIEE differently.</div>
<div>
<br /></div>
<div>
<a href="http://obinsight.blogspot.com/2014/01/case-study-how-to-implement-object_15.html">So next time</a> I will get into details of the things to be done at the DB level and RPD level for the actual implementation.</div>
<div>
<br /></div>
<div>
Stay tuned</div>
<div>
</div>
<div>
</div>
<div>
<br /></div>
<div>
<br /></div>
Bojiehttp://www.blogger.com/profile/13163354520521690612noreply@blogger.com0