Wednesday, October 16, 2013

Super Fast Saves with Relational Writeback for IBM Clarity

When Is It Useful?

Clarity is a budgeting and planning tool. As a budget deadline approaches, system usage skyrockets. Everyone has left their budget entry until the last possible moment. For that one day, when everyone is furiously trying to finish updating and approving their budgets, some systems grind to a half. Heavy save activity on a Clarity system can result in locking. This is when one user needs to wait for another user to finish saving before their changes can be saved. If there are only a few users on the system, it's not a big deal. When you have 20 people in the system all at the same time, where seconds seem like hours, this can be very frustrating and costly. Relational writeback options try to address this by optimizing the way data is saved back to the cube.

How Do I Do It?

There are some configuration steps to be accomplished before using relational writeback. A special writeback stored procedure is required too. This is something tailored to each cube to save the correct data back to the correct place in the fastest way. At the bottom is a sample writeback stored procedure for SQL 2008.

What Are The Different Writeback Types?

Writeback is a common term for the process of sending updates back to a cube. If a value in the is currently 100 and we want to update it 150, only the change (or delta) of 50 is sent back. We already have a row of data for the 100. We add a new row for 50, and that gives us the correct value of 150. Below is a list showing the various writeback methods. For anyone still using Clarity 6.x, there is a relational writeback for that version too, but it is not as reliable or fast as the one in Clarity 7.x.
  • 0 = Native Analysis Services Writeback: This lets SQL Server Analysis Services do all the work. It doesn't require any additional stored procedures, but is the slowest option.
  • 1 = Clarity custom write-back - cell by cell method: This method inserts the deltas to a relational table directly, one at a time. This is faster than the native method, but still slow because it loops through all the cells that changed, one at a time.
  • 2 = Clarity custom write-back - SQL 2005: For folks using SQL 2005, one method is known...but SQL 2005 is not officially supported in Clarity 7. This option should never be used, but it's an option, so I list it for reference.
  • 3 = Clarity custom write-back - SQL 2008: Last, but not least, this is the option most folks should select. This performs a fast writeback by doing an update in place. It also processes the changes as a set in one operation. This should always be the fastest option.

How Do I Know What Fields To Put In The Writeback Table, In What Order?

Clarity runs the following SQL Query to get the list of fields to be saved. The 'Amount' field should be first, and then each of the dimension fields returned by this query. The ordering of the dimension fields isn't important, but the names must match exactly. Also, if there are alternate hierarchies, they need to have a fake dimension column too for this writeback.

Select DimensionName
FROM Dimensions
WHERE CubeID IN (
  SELECT CubeID FROM Cubes
  WHERE lower(Alias) = '<cube alias from olap manager>'
 )
ORDER BY 
DimensionID

What are the different approaches?

Everything can be handled using a SQL stored procedure, but another approach is to setup two views that make the stored procedure much simpler. By setting up one view unioning all non-ROLAP partitions together and one view onto the writeback table, all the grouping stuff can be encapsulated into the view. This makes it a bit easier when building the stored procedure as you don't need to group everything in each part.

What If I Need Help?

It is very common to need a little help implementing this feature. I recommend contacting Clear Insight ( http://www.clearinsight.ca ) for help. They can help implement this for you and have you on your way quickly.

--Sample Writeback Stored Procedure
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[stp_ClarityWB_2008]
/*This parameter must be named @WB_Amount*/
@WB_Amount <cube name>_WBTableType READONLY
AS
BEGIN
DECLARE @Message NVARCHAR(4000);
DECLARE @MemberName NVARCHAR(100);
DECLARE @Measure nvarchar(100);
/*CONFIGURABLE TRANSACTION ISOLATION LEVEL*/
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
begin transaction
--superset WB table for all measure groups
CREATE TABLE #WB (
[Amount] [float] NULL,
[Account] [varchar](80) null,
[Scenario] [varchar](80) null,
[Measure] [varchar](80) null
)
CREATE UNIQUE CLUSTERED INDEX [IX_#WB] ON [dbo].[#WB]
(
[Account] ASC,
[Scenario] ASC,
[Measure] ASC
)
--insert transaction data into temporary table
insert into #WB([Amount],[Account],[Scenario],[Measure])
select SUM(coalesce([Amount],0.0)),[Account],[Scenario],[Measure]
from @WBTYPE
group by [Account],[Scenario],[Measure]
--determine measure group
select top 1 @Measure=[Measure] from #WB
if (@Measure='Amount') begin
--Perform writeback for Measure Group Amount
--ensure only one measure from measure group is being written to
SET @MemberName=''
select TOP 1 @MemberName=measure from #WB where Measure<>'Amount'
if (@@ROWCOUNT>0) BEGIN
SELECT @Message = N'Incorrect Measure - ' + @MemberName + ';Cannot write to
multiple Measures from same dimension';
ROLLBACK TRANSACTION
RAISERROR (@Message,16,1);
RETURN;
END
--Validate account dimension to ensure it exists in dim table and is a leaf
member
SET @MemberName=''
SELECT @MemberName=WB.[account] FROM #WB WB where WB.[Account] not in (select
[Level0_MemberId] from tbldimaccount)
-- check for Unknown member values
if (@@ROWCOUNT>0) BEGIN
SELECT @Message = N'Cannot write data to an Undefined, All or Parent Account
(' + @MemberName + ')';
ROLLBACK TRANSACTION
RAISERROR (@Message,16,1);
RETURN;
END
--Validate scenario dimension to ensure it exists in dim table and is a leaf
member
SET @MemberName=''
SELECT @MemberName=WB.[scenario] FROM #WB WB where WB.[scenario] not in
(select [Level0_MemberId] from tbldimscenario)
-- check for unknown member values
if (@@ROWCOUNT>0) BEGIN
SELECT @Message = N'Cannot write data to an Undefined, All or Parent Scenario
(' + @MemberName + ')';
ROLLBACK TRANSACTION
RAISERROR (@Message,16,1);
RETURN;
END
--Delete records from writeback table that will be updated by Clarity
DELETE
FROM dbo.Rolap_Budget
WHERE EXISTS(
SELECT *
FROM #WB WB
WHERE dbo.Rolap_Budget.[Account]=WB.[Account] and dbo.Rolap_
Budget.[Scenario]=WB.[Scenario])
--Insert deltas for Clarity update into WB table
insert into Rolap_Budget ([Amount],[Account],[Scenario],sourcedetail,dtstamp)
select sum(WB.[Amount])-
sum(coalesce(FactGroup.[Amount],0.0)),WB.[Account],WB.[Scenario], system_
user,GETDATE()
from #WB WB left outer join
(
select fact.[Account],fact.[Scenario],SUM(coalesce(fact.amount,0.0)) as
Amount
from
(
select * from tblfactactual UNION ALL
select * from tblfactbudget
) fact
group by fact.[Account],fact.[Scenario]
) factGroup
ON WB.[Account] = factgroup.[Account] and WB.[Scenario] =
factgroup.[Scenario]
group by WB.[Account],WB.[Scenario]
having sum(WB.[Amount])-sum(coalesce(FactGroup.[Amount],0.0))<>0
end
ELSE IF (@Measure='Count') BEGIN
--Perform writeback for Measure Group Count
--ensure only one measure from measure group is being written to
SET @MemberName=''
select TOP 1 @MemberName=measure from #WB where Measure<>'Count'
if (@@ROWCOUNT>0) BEGIN
SELECT @Message = N'Incorrect Measure - ' + @MemberName + ';Cannot write to
multiple Measures from same dimension';
ROLLBACK TRANSACTION
RAISERROR (@Message,16,1);
RETURN;
END
--Validate account dimension to ensure it exists in dim table and is a leaf
member
SET @MemberName=''
SELECT @MemberName=WB.[account] FROM #WB WB where WB.[Account] not in (select
[Level0_MemberId] from tbldimaccount)
-- check for Unknown member values
if (@@ROWCOUNT>0) BEGIN
SELECT @Message = N'Cannot write data to an Undefined, All or Parent Account
(' + @MemberName + ')';
ROLLBACK TRANSACTION
RAISERROR (@Message,16,1);
RETURN;
END
--Delete records from writeback table that will be updated by Clarity
DELETE
FROM dbo.Rolap_Budget
WHERE EXISTS(
SELECT * from #WB WB
where dbo.Rolap_Budget.[Account]=WB.[Account])
--Insert deltas for Clarity update into WB table
insert into Rolap_Budget_Count ([Amount],[Account],sourcedetail,dtstamp)
select sum(WB.[Amount])-sum(coalesce(FactGroup.[Amount],0.0)),WB.[Account],
system_user,GETDATE()
from #WB WB left outer join
(
select fact.[Account],SUM(coalesce(fact.amount,0.0)) as Amount
from
(
select * from tblfactactualcount
) fact
group by fact.[Account]
) factGroup
on WB.[Account] = factgroup.[Account]
group by WB.[Account]
having sum(WB.[Amount])-sum(coalesce(FactGroup.[Amount],0.0))<>0
END
ELSE BEGIN
SELECT @Message = N'Measure "' + @Measure + '" not defined.';
RAISERROR (@Message,16,1);
RETURN;
END
--drop stored proc temp table
DROP TABLE #WB
commit transaction
END



Thursday, September 12, 2013

What If Modelling in Clarity


What If Modelling is a great feature of Clarity. The challenge for this is in the setup of the tool. Each cube is different. Each team has a different type of modeling they want to do. At a high level, what if modeling enables power users to copy a (scenario, year) combination into another (scenario, year) combination and apply assumptions to it. "What If" Revenue doubles? "What If" Expenses go down by 50%? "What If" salaries go up by 50%?  For companies wanting an optimistic and pessimistic version of their Forecast or Budget, What If analysis is a great fit. With the touch of a button, optimistic an pessimistic versions can be updated from the latest Forecast or Budget, with the same assumptions applied on top of those most recent numbers, providing a true comparison between these scenarios.

Line Item Detail in Clarity


Line Item Detail is a powerful feature, but not often used in Clarity.  Line Item Detail allows users to enter detail below the lowest grain of the cube.  The most common use is for budget planning. Most cubes have an Account dimension that matches their chart of accounts. Some users may know that they have a few separate items they want to track, that all rollup into this account. For example, let's say we are planning insurance costs for an office, and there is only one Insurance account in the chart of accounts. By using Line Item Detail, a user could enter a line item for each type of insurance on the template. They get the detail they need for tracking, and the cube does not need to be changed. This can provide great flexibility when we just need a bit more detail on a given template.

Where is the documentation for my version of Clarity?

The IBM website can be a bit intimidating due to the size and quantity of products they support. How can we find documentation for our specific version of Clarity? Here are some steps to find the documentation for your version of Clarity. Some versions do not have full documentation. In that case, please try to find the documentation for the next lowest version. Here is a link directly to this section on the IBM site for Clarity 7.2.1FP2. If the link is broken or a newer release is available, you will need to follow the steps below to find the documentation.

Clarity 7.2.1 FP2 Documentation http://www-01.ibm.com/support/docview.wss?uid=swg27024452

Step 1 - IBM.com
From IBM.com, select the 'Support & downloads' menu and then the 'Documentation' link under Technical Support.

Step 2 - Search for 'Clarity'
On the left side of the screen, enter 'Clarity' into the Quick find search box, and press enter.

Step 3 - Add your version of Clarity
By first adding a checkmark beside Clarity, you will be able to select your particular version of Clarity and the operating system. I recommend selecting just the product version and leaving the operating system selection blank. Once you make the selection and move it to the right, you can click the Finish button.

Step 4 - Click on 'Clarity ... Documentation'
Now you should be able to see a link for the documentation for your version. On this page, you will find the server admin, server install, end user and reference guides for all Clarity products at this version.

How do I get help from IBM on Clarity or TM1?

A common question we receive is, "How can I get help from IBM on Clarity or TM1?...and what do they cover?".  Companies current on their support contracts with IBM are entitled to support from them. The support is limited to making sure the base product works as expected. It does not include any custom development, templates or reports. IBM will be able to make sure you can connect to Clarity from Clarity Studio, but they will not help you build that new Balance Sheet or Profit and Loss report.

If there are challenges though, it's nice get their help. They are smart folks trying to help people. From the IBM home page, under the 'Support and downloads' menu, there is a menu item for 'PMRs and service requests'. This is the codeword for support. Going to this area, you will be able to enter a support ticket and get their help. You can also track status of any open support tickets here too.

Here is a link to the Service requests home page.



Updating Clarity Software For Free


Did you know that you are entitled to all Clarity software updates from IBM, as long as you are current on your maintenance contract?  Bug fixes and enhancements are released periodically as incremental updates. Many fixes improve stability and small annoying challenges for which folks commonly implement workarounds. At the time of this writing, the current version of Clarity is 7.2.1FP2. If you are on something older, you may be able to improve performance, stability and features by updating your server software.

Here is a link to the download site for the Clarity 7.2.1FP2 software. 

Most of the time internal Information Technology groups can perform the update without assistance by following the instructions provided. For folks wanting some support or assistance, Clear Insight (http://www.clearinsight.ca) is available to help. 



Filtering by Measures in TM1

Overview
When building applications on Cognos TM1, there is often a need to filter something by data in another cube for a user. In regular database applications, this would some form of a JOIN clause (INNER, LEFT OUTER, RIGHT OUTER). TM1 does not have those constructs, but not to worry. This can be done just as easily. This could be used when building a subset, or when querying a cube or dimension. In this example, I will start with a basic MDX subset definition and add pieces to it until it will serve as the MDX for building a sorted subset, filtered by data in a separate cube.

For this example, let's assume the following setup
*  We have a cube called c.Account.Balance with four dimensions (d.Account, d.Measure, d.Time, d.Year). *  This cube holds financial accounting information for a small company
*  d.Account containts an income statement chart of accounts
*  d.Measure has only one element called Amount
*  d.Time has 12 elements. One for each month Jan...Dec.
*  d.Year has 3 elements. One for each fiscal year of interest, Y2013, Y2014, Y2015
* We want a list of all accounts with a January, Y2014 balance greater than $1000

Step 1 - Build a simple dimension subset of all members

{ TM1SUBSETALL( [d.Account] ) }

Step 2 - Restrict the subset to only leaf members. These are also known as N-level or bottom level members.

{ TM1FILTERBYLEVEL( { TM1SUBSETALL( [d.Account] ) } ,0) }

Step 3 - Use the MDX FILTER function to restrict...or join... the dataset. Note how we do NOT need to specify anything for the d.Account member. TM1 is smart enough to know we are building a set of accounts from a cube filter.

{ FILTER( 
  { TM1FILTERBYLEVEL( { TM1SUBSETALL( [d.Account] ) } ,0) }
 ,[c.Account.Balance].( [d.Measure].[Amount], [d.Time].[Jan], [d.Year].[Y2014]) > 1000)

Step 4 - Add sorting with the MDX ORDER function. 

{ORDER(
 { FILTER(
    { TM1FILTERBYLEVEL( { TM1SUBSETALL( [d.Account] ) } ,0) }
  ,[c.Account.Balance].( [d.Measure].[Amount], [d.Time].[Jan], [d.Year].[Y2014]) > 1000)
 , [d.Account] ASC ) }

Done!

Now we have build an MDX statement to give us a list of accounts with a January Y2014 balance greater than $1000, sorted by the Account name.