Building a Hyperion Home Lab: Choosing Your Motherboard

Before we get started, here’s the entire series in case you need to look back (or ahead):

At this point, hopefully you have decided on what processor you would like to go with.  But, if you haven’t, perhaps this post will help you with that decision.  As we look at different motherboard options, we’ll stick with the plan of desktop and server options with budgets for low, medium, and high.

So how do you select a motherboard?  When it comes to Hyperion, we are interested in memory capacity first and foremost, then storage options, and finally general expansion.  The entire purpose for our home lab is to learn more.  Learning more generally means we have more virtual machines running more versions of Hyperion.  This means we need more memory to keep those different virtual machines running.  In our desktop options, we’ll see that we will be limited to four (4) memory slots that will max out at 64 GB of RAM.  We should see some better options once the next generation of high-end desktop processors and motherboards come out, but for now, this is what we have to work with and it is still double what most laptops will offer.

But…that still might not be enough for you.  That leads us to our server options.  Basically if you want more cores and more memory…go the server route.  Now on to the options:

Desktop High Budget

As we look at each of our desktop budget options, the main difference will be the chipset that each motherboard is based on.  For our high-budget option, we’ll be looking at an Intel Z170-based motherboard from ASRock.  The ASRock Z170M Extreme4 is a Micro ATX (this will be important when we get to chassis selection) motherboard with four (4) memory slots, Intel LAN, three (3) PCIe x16 slots, USB 3.1 support, and an Ultra M.2 slot.  This means we have the best expansion options available.

Additionally, with Intel LAN, ESXi now supports this motherboard.  The Z170 also supports overclocking, if that’s your thing.  The Z170M Extreme 4 is priced at $124.99.

Desktop Medium Budget

Our medium budget option is very similar to the high budget option, but at a significant cost savings.  Based on the Intel H170 chipset, the ASRock H170M Pro 4 is also Micro ATX.  This board has four (4) memory slots, Intel LAN, two (2) PCIe x16 slots, and an Ultra M.2 slot.  So with one less PCIe slot and no USB 3.1, it has less capacity for expansion.  The H170M Pro 4 is price at $84.99.

Desktop Low Budget

Our low budget option is basically the minimum required to set up your home lab.  Based on the Intel H110 chipset, the ASRock H110M-HDS is also Micro ATX.  This board has four (4) memory slots, Realtek LAN, and one (1) PCIe x16 slot.  Basically we lose Intel LAN (necessary for ESXi), another PCIe slot, and an Ultra M.2 slot.  We can basically only add one significant device to this board (either a PCIe SSD or a different network card).  But, at $51.99, at least it has low cost going for it!

Server High Budget

Interestingly, our high budget option does not give us the highest level of performance.  As you will see once we get to the medium budget option, Ebay can be a powerful tool in the construction of a home lab.  For those that are not comfortable with used hardware and would prefer to just get new gear quickly from Newegg or Amazon, this option is for you.

Based on the Intel C612 chipset, the Supermicro MBD-X10DRI is our selection for the high budget option.  This board is Extended ATX (massive) and is packed with plenty of features including sixteen (16) memory slots, dual Intel LAN, USB 3.0, three (3) PCIe x16 slots, and three (3) PCIe x8 slots.  Given the number of PCIe slots, the exclusion of the M.2 slot isn’t surprising.  Chances are, if you go with this option, you will be looking at Intel PCIe SSD’s anyway.

All of our server boards will also have something called IPMI on board with KVM over LAN.  This is basically a web-based management system that allows you to power your server on and off, reset it, see vital statistics, and even have access to the mouse, video, and mouse over the network.  This enables our servers to be completely headless (no monitor, or any other devices for that matter).  So if you wanted to say, stick it in a closet…no worries.

The X10DRI is priced at a hefty $429.99, but again, it has great features and will last a really long time.

Server Medium Budget

This is my favorite options.  The ASRock EP2C602-4L/D16 motherboard is based on the Intel C602 chipset and also includes a great many features.  It also has sixteen (16) memory slots (though DDR3 instead of DDR4), quad Intel LAN, four (4) PCIe x16 slots, and one (1) PCIe x4 slots.  As with all of our server options, this board is also equipped with IPMI and KVM over LAN.  At $309.99, it makes for an excellent match to our Ebay-purchased E5-2670 processors.  And as we’ll see in our upcoming memory post, there are some great deals to be had for memory that works in this board.

Server Low Budget

For our low budget option, I’ve chosen a single-processor motherboard to support our Xeon V5 processor.  The Supermicro X11SSH-F-O is based on the Intel C236 chipset and supports socket 1151 processors (include Core i5 and Core i7 processors).  The board has four (4) memory slots, dual Intel LAN, USB 3.0, one (1) PCIe x16 slot, and two (2) PCIe x8 slots.  So why would this board be better than our desktop models?  First, it is a server board, that’s what it was made for.  Second, it has IPMI and KVM over LAN.  This feature alone makes its far superior if you want a truly headless system.  Third, it supports ECC RAM.  Clocking in at $218.48, it is also the cheapest board by far of our server options.

Tiny Server

Finally, if you want a really tiny server that packs a punch, we have our Tiny Server option.  Based on an integrated Intel Xeon-D 1540, the Supermicro MBD-X10SDV-4C-TLN2F-O (wow…nice name), packs a lot of punch in a tiny format.  With four (4) memory slots, dual Intel LAN, USB 3.0, and one (1) PCIe x16 slot, this board still has quite a few options.  It also support IPMI and KVM over LAN along with a maximum memory size of 128 GB.  It does have very limited memory support to reach that capacity, but we’ll get to that soon enough.  Clocking in at $0.00, since it comes with the processor ($489.99), this board is a steal!

There are also other variations of this board with 10 GB LAN and an eight (8) core processor, but the price is nearly double that of this board.

That’s it for motherboards.  Here’s a quick summary of the motherboards in a table:.

MotherboardPriceLink
ASRock Z170M Extreme4$124.99Newegg Link
ASRock H170M Pro4$84.99Newegg Link
ASRock H110M-HDS$51.99Newegg Link
Supermicro MBD-X10DRI$429.99Newegg Link
ASRock EP2C602-4L/D16$309.99Newegg Link
Supermicro X11SSH-F-O$229.99Newegg Link
Supermicro MBD-X10SDV-4C-TLN2F-O$0.00Newegg Link

Building a Hyperion Home Lab: Choosing Your Processor

Before we get started, here’s the entire series in case you need to look back (or ahead):

Now that we’ve covered your Hypervisor choices, hopefully you’ve decided a direction you would like to go.  For those of you that want to stick with a desktop implementation that you can use for things outside of a lab, your CPU choice will likely differ from those of us that go dedicated bare metal.  So what are the key features of a processor that we will look at from a Hyperion Perspective:

  • Single-Threaded vs. Multi-Threaded Performance
  • Power and Heat Constraints
  • Size Constraints
  • Cost and Longevity

When we look at Hyperion environments, we know that there are a great many services.  On top of the number of services, we also know that most of the software does a great job of multi-threading.  This means that when we look at building an environment for Hyperion, we will care more about multi-threaded performance than single-threaded performance.  One drawback to more cores in a processor is generally that it lowers the operating frequency of each core.  And of course, as we add more cores, we add more cost.  Generally speaking, if you are going with a desktop system, I would recommend getting at least a quad-core processor.  If you are looking at server-based options, there are a lot more choices.

Power and heat constraints will also drive our processor decision.  If you plan on running your home lab in your home office, you will likely prefer something that doesn’t sound like a leaf blower and doesn’t take up too much space.  The newer the processor, the lower the total energy consumed and heat produced will be.

The size of the system will also determine where you can realistically place the system.  Will it fit on or under your desk?  Will it need to be placed in a closet somewhere?  The size of the system will of course have an impact on the amount of processing power, memory, and storage that you can contain in that system.

Finally, and probably most importantly, how much does it cost and how long will it last?  Ideally, we’d like a system to last several years and we want to spend an amount that lets us get the most bang for our buck.  The processor we select will also have a massive impact on the total cost of the system.  In this series we will cover seven (7) configurations in an attempt to find a variation for most budgets and needs: Desktop High Budget, Desktop Medium Budget, Desktop Low Budget, Server High Budget, Server Medium Budget, Server Low Budget, and Tiny Server.

Before we get into the specific processor details, let’s first talk about why we would want to go the desktop route versus the server route.  The desktop configurations can be used for a variety of things, not just our lab.  Our high budget desktop system can be used in either configuration as the hardware is supported.  You will always have a monitor, mouse, and keyboard attached to these systems.  They will likely be place where you work or have family members using them.  Our server configurations are built for one purpose…hosting VM’s.  All of the options we will cover can be completely headless (no need for a monitor, mouse, or keyboard).  They will also come with hardware completely supported by ESXi and other bare-metal options.

On to the options:

Desktop High Budget

The processor I would choose right now in the high end budget configuration is the Intel Core i7 6700k.  This processor is based on the latest Intel Skylake architecture and supports up to 64GB of DDR4 memory.  It has four (4) physical cores with hyper-threading support giving us eight (8) logical processors operating at 4.0 GHz.  The current price of this processor is $416.99 on Newegg and $413.99 on Amazon.  If you happen to have a Microcenter nearby, you can get this process for $399.99 along with an additional $20 discount if you bye a motherboard there as well.

Desktop Medium Budget

For the medium budget configuration, I would choose the Intel Core i7 6700.  This is basically the same as the 6700k but with no overclocking features and a lower operating fequency (3.4 GHz vs. 4.0 GHz).  The current price of this processor is $349.99 on Newegg.  If you happen to have a Microcenter nearby, you can get this process for $339.99 along with an additional $20 discount if you bye a motherboard there as well.

Desktop Low Budget

For our lowest budget configuration, I would choose the Intel Core i5 6400.  This processor is still has four (4) physical cores, but does not support hyper-threading.  It operates at 2.7 GHz.  The current price of this process is $189.99 at Newegg.  Microcenter does not carry this particular processor, but they do have the 6500 for $199 with the $20 promotion.  There are much cheaper processors in the Skylake product line, but once we get below four cores, you may as well stick with your laptop.

Server High Budget

Our server processors will go across a much broader range of product choices.  Our desktop options are pretty much Skylake configurations.  Our server options span three different generations of processor.  The high budget processor that I would recommend is the Intel Xeon E5-2620 V3.  This processor is based on the Intel Haswell architecture and supports single and dual processor configurations.  I would recommend going with the dual processor configuration because after all, this is our high budget option.  The processor operates at 2.4 GHz with six (6) physical cores and twelve (12) logical threads.  This means that in a dual processor configuration we have 12 cores and 24 threads to play with.  The budget for such a beast?  $419.99 per processor at Newegg.  The processors will support up to 1.5 TB of RAM…so long as you have a few gold bars laying around to pay for it.

Server Medium Budget

Our medium budget option for a server is basically what I’ve done.  If you search on Ebay for Intel E5-2670 SR0KX you should find plenty of deals for cheap processors.  These are based on the Sandy Bridge architecture and can be had anywhere from $90 to $190 per processor depending on your patience level.  They have eight (8) physical cores and (16) logical threads operating at a frequency of 2.6 GHz.  In a dual processor configuration this gives us 16 real processors and 32 virtual processors.  They also use less expensive DDR3 RAM, which we’ll cover later.  The processors only support 768 GB of RAM, but let’s be honest, if you are lucky you will have 256 GB.  Most of us will end up with 64GB or 128 GB.

Server Low Budget

For our low budget server option, I would go with the Intel Xeon E3-1220 V5.  Based on the Skylake architecture operating at 3.0 GHz, it has four (4) physical cores without hyper-threading support.  Like our Core i7 and Core i5 options, this processor tops out at 64 GB of RAM but adds support for ECC memory.  If you don’t really care about ECC memory, you could stick with any of the Core i7 or Core i5 options above as most of the motherboards we’ll talk about later support both Xeon and Core processors.

Tiny Server

In general, most of the options above can be placed into a tiny server (mini ITX).  But, those options don’t support 128 GB of RAM.  Our tiny server option is the Intel Xeon D-1520.  This is a fully integrated processor that comes with the motherboard (built-in).  This processor has four (4) physical cores and does support hyper-threading.  Each core operates at 2.2 GHz.  It has all of the features we would normally see on our server-class boards like IPMI and Intel LAN.  The price for the board and processor is $489.99.  It’s also tiny…

That’s it for processors.  Here’s a quick summary of the processors in a table:

ProcessorPriceGHzPhysical CoresLogical ThreadsMax MemoryNewegg LinkArk Link
Intel Core i7 6700k$414.9944864Newegg LinkArk Link
Intel Core i7 6700$349.993.44864Newegg LinkArk Link
Intel Core i5 6400$189.992.74464Newegg LinkArk Link
Intel Xeon E5-2620 V32 @ $429.992.46121,536Newegg LinkArk Link
Intel Xeon E5-2670~2 @ $150.002.6816768Newegg LinkArk Link
Intel Xeon E3-1220 V5$218.4834464Newegg LinkArk Link
Intel Xeon D-1520$489.992.248128Newegg LinkArk Link

Next up…our motherboard.

 


Building a Hyperion Home Lab: Introduction and Choosing Your Hypervisor

Before we get started, here’s the entire series in case you need to look back (or ahead):

Welcome to the first in a series designed to help anyone interested in building their own virtual home lab for Oracle’s Hyperion EPM stack of software.  So why would anyone want build such a thing?  For me, there are two reasons.  First, to stay on the bleeding edge.  I like having the newest release up and running within a day or two of the release.  Second, I’m tired of giant laptops and starting up and shutting down software.  With my home lab, I just carry my reasonably-sized MacBook Pro and RDP into anything else I need.

Today, we’ll focus on the software we will use for our lab and then we’ll dive into the major hardware components in later posts.  Why do we start with software?  Because it will have a major impact on the hardware choices we make.  More on that later…

So how do I choose the Hypervisor that’s right for me?  First, let’s talk about what a Hypervisor is.  In short, a Hypervisor is a platform for creating and hosting Virtual Machines.  There are two main types of Hypervisors.  First, we’ll talk about the one’s many of you are already familiar with.  Those that run on a standard operating system like Windows or Mac OS X (and the few that use Linux as a desktop operating system).  Hosted Hypervisors, as they are called, are designed to allow a system host Virtual Machines, but not isolate that system into that single task.  You’ve most likely heard of the most popular of these:

VMware Workstation (and Fusion) is my particular favorite.  I work a lot with VMware ESXi and it allows for me to manage my ESXi servers, work with my local VM’s, and transfer back and forth between the two.  It also works great on both Windows and Mac OS X platforms.  This means I can work on my VM’s on my Windows-based desktop and my MacBook Pro.  But, it is definitely not free, which is clearly a drawback.

As a free option, we have Oracle VirtualBox.  While VirtualBox is technically an Oracle product, it still operates somewhat outside of Big Red.  It’s a great piece of software, and it also works across both Windows and Mac OS X.  But, it doesn’t interface with ESXi and if I want to move VM’s back and forth, I have to convert them each time.  For me, this kills it as an option for my day-to-day use.

Finally, we have the Mac-only option of Parallels.  The lack of interoperability kills this for many people, but for those that plan on using their Mac, this is a great option, and it interfaces great with Mac OS X.  Like VMware, Parallels is not free.  It it moderately less expensive than VMware, but there is still an investment.

Now that we’ve covered the options available for our desktops and laptops, let’s move on to the good stuff:  Bare Metal.  The idea behind bare metal is to provide a system with the singular purpose of high performance virtual machines.  Of course this means that the system does absolutely nothing else.  This also means that we are venturing out of your typical consumer products and into the land of enterprise products.  So why does that matter?  Because when you get into enterprise products, the hardware that you can use starts to shrink due to the vendor support of that hardware.  Here are a few of the most popular bare metal options:

Again, I have to go with VMware as my favorite.  For many of the reasons above, but honestly, for one main reason:  virtually every client I have uses it.  I have a scarce few that use Hyper-V and none using anything else.  VMware also has a free Hypervisor that works great.  If you need functionality beyond the free version, you can spend $200 and get the entire suite of products for your home lab using the VMUG Advantage program.  The biggest drawback to VMware’s bare metal option is that hardware support is much more challenging.

Microsoft Hyper-V has two versions available.  There is a headless version and the role that you can add to Windows Server 2012 R2.  Both provide a high quality solution with probably the best hardware support out there.  If I wasn’t a VMware fan, this would be my next choice.  I’ve previously run my lab on Hyper-V and it was a good experience.  But, the lack of interoperability with my desktop and laptop along with the lack of clients made the switch to VMware the logical decision for me personally.

Oracle VM Server is an interesting option.  You can use this free of charge for some period of time so long as it isn’t in production.  There are a variety of Oracle provided VM’s that are native to this Hypervisor.  But again…few clients actually use this technology.  And the community at large is much, much smaller.  ESXi and to a less extent, Hyper-V have huge communities of people that can help you for free.  Not so much on the Oracle VM side of the house.

Finally we have the Xen Project.  This is another free option that is open source.  I’ve not had a ton of experience with this Hypervisor, but I’ve always heard good things.  It has a decent community, but for me it just didn’t make sense to go with a technology that few, if any of my clients were going to be using.

So there you have it…a lot of choices.  Desktop…or bare metal.  VMware…or the other guys.  As this series continues, I’ll reference the options at a high-level to help with the decisions surrounding hardware selection.  While you wait for my next post on the topic, you can check out a few websites with a wealth of knowledge:

  • TinkerTry – A website devoted to home labs
  • ServeTheHome – This is less on the software side and more on the hardware side, but has a great forum for support and deals
  • VMWare Communities – Similar to what we are used to with the Oracle Forums
  • Derek Seaman’s Blog – He has a great set of tutorials and is a very active blogger
  • Vladin – Another active blog with great information

 


Essbase Format Strings in Smart View, The Excel Add-In, and Financial Reports

Last year I had a post about working with strings in Essbase.  To illustrate how to work with strings, I used a features called Essbase Format Strings.  This basically allows us to return a value other than that of the intersection based on MDX logic.  For instance, if a number is great than zero, return the text Positive or if it less than zero, return Negative.  This allows us to show text values inside of an Essbase retrieve.

Today we will dive a little bit deeper into how this works in each of our popular interfaces with Essbase:  Smart View, the Excel Add-In, and Financial Reports.  First we’ll look at the classic Excel Add-In.  This is pretty simple in that we really have no control over what we show.  The Excel Add-In will only show the value of the format string:EssbaseFormatStrings1

As we can see, the value of the format string is displayed.  There are no settings to turn that off in the Excel Add-In (that I can find).

Next up, we have Smart View.  Smart View is a little bit more aware of the concept of Essbase Format Strings.  It actually has a setting that we can turn on and off.  Using the default settings, we get this:

EssbaseFormatStrings2

This looks nearly identical to the Excel Add-In retrieve.  Now let’s look in our Smart View options:

EssbaseFormatStrings3

Here we see an option to enable and disable our format strings.  So what happens when we turn this setting off?

EssbaseFormatStrings4

Now we see the 0 that we configured as the value to be returned by our calculated member, instead of the format string.  So what does this look like in Financial Reports?EssbaseFormatStrings5

We’re back to the format string showing!  And like the Excel Add-In, Financial Reports just displays the value of the format string and there’s no way to turn this off.  Hopefully this provides a little clarity as to how Format Strings work and how we can display them in our various popular interfaces.

 


New Year, New Blog Posts

It’s been a little longer than I would like between posts lately, but with the holidays..that’s life.  The good news is that I’ve been doing plenty of work, just nothing post-worthy.  So what are some of the accomplishments that didn’t merit a post but should result in a ton of content:

  • Added a new server to the lab to both add capacity and allow for some initial physical versus virtual performance testing and tuning (update to the lab page coming)
  • Began the process of upgrading to VMware ESXi 6.0
  • Began upgrading the processors on the main ESXi box to Xeon E5-2670’s (8-core/16-thread)
  • Upgraded VMware vSphere to 6.0.
  • Was notified that I will be speaking at Kscope16 (seven years in a row as a KScope presenter!)
  • Began preparing a ton of content for the new year

And on that note, I have a ton of interesting (at least to me) content coming up this year:

  • Continue the Planning Repository series
  • Continue the Powershell series
  • New series on Building Your Own Hyperion Lab (more hardware focused, less software focused)
  • New series on Performance Tuning Essbase (more software implementation of the hardware side)
  • Any other fun topics that pop up or any questions I get from colleagues and readers

That’s it for now.  Look for more updates hopefully on a more frequent basis now that we are past the holidays.  And finally…Happy Belated New Year!


PowerShell and Hyperion: Introduction

It’s been a little while since my last post.  Things have managed to get busy both at work and at home, but I’m happy to get back to it tonight.  This post will focus on providing an introduction to PowerShell and how we can use it with Hyperion products.

So what is PowerShell and why do I care?  If you use *nix…you don’t.  But for those of us that have our Hyperion installations in a Windows environment, you should!  In short, PowerShell is a powerful shell built into most modern versions of Windows (both desktop and server) meant to provide functionality far beyond your standard batch script.  Imagine a world where you can combine all of the VBScript that you’ve linked together with your batch scripts.  PowerShell is that world.  PowerShell is packed full of scripting abilities that make things like sending e-mails no longer require anything external (except a mail server of course).

Where do I get PowerShell?  Chances are, you already have it.  If you are running Windows 7 or above, it is included by default.  If you are running Window Server 2008 R2 or above, it is included by default.  If for some reason you still don’t have it, go get it here:

https://www.microsoft.com/en-us/download/details.aspx?id=40855&WT.mc_id=rss_alldownloads_all

The building blocks of PowerShell are called cmdlets.  Cmdlets can do things as simple as a directory listing or file copy and as complex as parsing logs and sending e-mails when errors occur.  So let’s start by taking a look at a very simple PowerShell script:

#############################################################
#Created By:	Brian Marshall
#Created Date:	11/8/2015
#Purpose:		Sample PowerShell Script for HyperionEPM.com
#############################################################

dir

So that’s pretty simple.  It looks very similar to a normal batch file.  This just has comments at the beginning to let us know what the script is followed by a single command: dir.  Before we dig a little deeper, let’s just try to execute this script:

Blog1Script1Capture1

Ok..maybe not simple?  If you are running this on Windows Server 2008 R2 (or above), you will get the above.  By default, it does not allow you run execute your own PowerShell scripting files.  So let’s change that setting.  Open a PowerShell window as an Administrator and execute this command:

Set-ExecutionPolicy RemoteSigned

Answer yes (Y) and we should be ready to try our code again:

Blog1Script1Capture2

And the script results:

Blog1Script1Capture3

Alright…now we’re headed in the right direction.  So what just happened?  It looks just like a batch script, right?  Looks can be deceiving.  What actually happened then?  In PowerShell, the dir command is an alias for another command.  Just like members can have aliases in the Hyperion world, cmdlets can have aliases in the PowerShell world.  So what does dir actually do then?  It executes the Get-ChildItem cmdlet.  This cmdlet was intended to replicate the dir command functionality and adds a little more to it.  If you are really interested in the Get-ChildItem cmdlet, check out TechNet for more information here:

https://technet.microsoft.com/en-us/library/ee176841.aspx

Now let’s try something we actually care about…MaxL!  Let’s start with something simple, like starting MaxL from PowerShell:

###############################################################################
#Created By:	Brian Marshall
#Created Date:	11/8/2015
#Purpose:		Sample PowerShell Script for HyperionEPM.com
###############################################################################

###############################################################################
#Variable Assignment
###############################################################################

$MaxLPath = "C:\Oracle\Middleware\user_projects\epmsystem1\EssbaseServer\essbaseserver1\bin"

###############################################################################
#MaxL Execution
###############################################################################

& $MaxLPath\StartMaxL.bat

Looking at the script, we see that we first assign a variable.  The syntax for this is simple (and similar to MaxL).  We prefix the variable name with a dollar sign and set it equal to a value.  Once the path to MaxL has been set to our variable, we can then execute MaxL.  Notice that we use an ampersand followed by the variable for the path and then the rest of the path to the executable is just plain text on the line.  So what happens?

Blog1Script1Capture4

As expected…MaxL starts.  Now let’s do something slightly more complex (but still pretty simple) and actually execute a MaxL script:

###############################################################################
#Created By:	Brian Marshall
#Created Date:	11/8/2015
#Purpose:		Sample PowerShell Script for HyperionEPM.com
###############################################################################

###############################################################################
#Variable Assignment
###############################################################################

$MaxLPath = "C:\Oracle\Middleware\user_projects\epmsystem1\EssbaseServer\essbaseserver1\bin"
$MaxLUsername = "admin"
$MaxLPassword = "password"
$MaxLServer = "hyperiones"

###############################################################################
#MaxL Execution
###############################################################################

& $MaxLPath\StartMaxL.bat Blog1MaxL1.msh $MaxLUsername $MaxLPassword $MaxLServer

We’ve established a few more variables and chosen a MaxL file to execute.  Let’s look athe MaxL while we’re at it:

login $1 identified by $2 on $3;

logout;

Ok…the world’s simplest MaxL script.  And the results:

Blog1Script1Capture5

And there we have it!  We’ve successfully integrated MaxL and PowerShell.  Up next…we’ll start looking into combining Essbase and Planning…on different servers!  While you wait, you can check out more PowerShell information here:

https://technet.microsoft.com/en-us/library/dd772285.aspx


The Planning Repository: HSP_MEMBER (Part 6 – Putting It All Together)

Not to sound like a broken record, but…a few disclaimers:

  • This is the fun stuff…that voids your warranty.  Not that you have to tell Oracle that you poke around here, but if you do, they will blame anything and everything that’s wrong on you for being in there.
  • The content here has been tested and validated against 11.1.2.3.500 and 11.1.2.4.
  • The content here has been tested and validated against Microsoft SQL Server and Oracle 11g.
  • The content here is based on the Vision sample application.
  • The content here is totally unsupported by Oracle (in case you missed the first bullet).

We’ve finally made it to the last part in our series on the HSP_MEMBER table (and all of the many associated tables)!  In this post, we’ll take everything that we’ve learned and combine it to get a full extract of a single custom dimension for a single plan type.  In our SQL Server query, this is a pretty straight forward operation.  We simply combine the queries from the prior five parts of this series.

SQL Server:

SELECT
    o.OBJECT_NAME AS MEMBER_NAME
	,op.OBJECT_NAME AS PARENT_NAME
	,a1.ALIAS_NAME AS ALIAS_DEFAULT
	,a2.ALIAS_NAME AS ALIAS_ENGLISH
	,REPLACE(REPLACE(REPLACE((
		SELECT
			ou.UDA_VALUE AS UDA
		FROM 
			HSP_MEMBER_TO_UDA u
		INNER JOIN
			HSP_UDA ou ON ou.UDA_ID = u.UDA_ID
		WHERE
			u.MEMBER_ID = m.MEMBER_ID FOR XML Raw)
			, '"/><row UDA="', ', '), '<row UDA="', ''), '"/>', '' )
	 AS UDA
    ,CASE m.DATA_STORAGE
		WHEN 0 THEN 'Store Data'
		WHEN 1 THEN 'Never Share'
		WHEN 2 THEN 'Label Only'
		WHEN 3 THEN 'Shared Member'
		WHEN 4 THEN 'Dynamic Calc and Store'
		WHEN 5 THEN 'Dynamic'
	 END AS DATA_STORAGE
	 ,CASE m.DATA_TYPE
		WHEN 0 THEN 'Unspecified'
		WHEN 1 THEN 'Currency'
		WHEN 2 THEN 'Non-currency'
		WHEN 3 THEN 'Percentage'
		WHEN 4 THEN 'Enum'
		WHEN 5 THEN 'Date'
		WHEN 6 THEN 'Text'
		ELSE 'Unspecified'
	 END AS DATA_TYPE
	,CASE WHEN m.USED_IN & 1 = 1 THEN 
		CASE 
			WHEN m.CONSOL_OP & 6 = 6 THEN '^' 
			WHEN m.CONSOL_OP & 5 = 5 THEN '~' 
			WHEN m.CONSOL_OP & 4 = 4 THEN '%' 
			WHEN m.CONSOL_OP & 3 = 3 THEN '/' 
			WHEN m.CONSOL_OP & 2 = 2 THEN '*' 
			WHEN m.CONSOL_OP & 1 = 1 THEN '-' 
			ELSE '+' END
		ELSE
			NULL
		END AS PLAN1_CONSOL_OP
	,CASE WHEN mfp1.FORMULA IS NULL THEN mfpd.FORMULA ELSE mfp1.FORMULA END AS PLAN1_FORMULA
	,CASE WHEN mfp1.DATA_STORAGE IS NULL THEN mfpd.DATA_STORAGE ELSE mfp1.DATA_STORAGE END AS PLAN1_DATA_STORAGE
	,CASE WHEN mfp1.SOLVE_ORDER IS NULL THEN mfpd.SOLVE_ORDER ELSE mfp1.SOLVE_ORDER END AS PLAN1_SOLVE_ORDER
	,at.ATTRIBUTE_NAME AS ATTRIBUTE_TEXT
	,ab.ATTRIBUTE_NAME AS ATTRIBUTE_BOOLEAN
FROM
	HSP_MEMBER m
INNER JOIN 
	HSP_OBJECT o ON m.MEMBER_ID = o.OBJECT_ID
INNER JOIN
	HSP_OBJECT op ON o.PARENT_ID = op.OBJECT_ID
INNER JOIN
	HSP_OBJECT od ON m.DIM_ID = od.OBJECT_ID
LEFT JOIN
	(	SELECT
			oa.OBJECT_NAME AS ALIAS_NAME
			,a.MEMBER_ID
		FROM
			HSP_ALIAS a
		INNER JOIN
			HSP_OBJECT oa ON a.ALIAS_ID = oa.OBJECT_ID
		INNER JOIN
			HSP_OBJECT oat ON a.ALIASTBL_ID = oat.OBJECT_ID
		WHERE
			oat.OBJECT_NAME = 'Default')
	 a1 ON m.MEMBER_ID = a1.MEMBER_ID
LEFT JOIN
	(	SELECT
			oa.OBJECT_NAME AS ALIAS_NAME
			,a.MEMBER_ID
		FROM
			HSP_ALIAS a
		INNER JOIN
			HSP_OBJECT oa ON a.ALIAS_ID = oa.OBJECT_ID
		INNER JOIN
			HSP_OBJECT oat ON a.ALIASTBL_ID = oat.OBJECT_ID
		WHERE
			oat.OBJECT_NAME = 'English')
	 a2 ON m.MEMBER_ID = a2.MEMBER_ID
	LEFT JOIN
	(SELECT
		MEMBER_ID
		,mf.PLAN_TYPE
		,CASE DATA_STORAGE
			WHEN 0 THEN 'Store Data'
			WHEN 1 THEN 'Never Share'
			WHEN 2 THEN 'Label Only'
			WHEN 3 THEN 'Shared Member'
			WHEN 4 THEN 'Dynamic Calc and Store'
			WHEN 5 THEN 'Dynamic' END AS DATA_STORAGE
		,SOLVE_ORDER
		,FORMULA
	FROM
		HSP_MEMBER_FORMULA mf
	LEFT JOIN
		(SELECT 0 AS PLAN_TYPE,'Default' AS TYPE_NAME UNION ALL 
		 SELECT PLAN_TYPE,TYPE_NAME FROM HSP_PLAN_TYPE) p ON mf.PLAN_TYPE = p.PLAN_TYPE
	WHERE
		p.TYPE_NAME = 'Default') mfpd ON m.MEMBER_ID = mfpd.MEMBER_ID
LEFT JOIN
	(SELECT
		MEMBER_ID
		,mf.PLAN_TYPE
		,CASE DATA_STORAGE
			WHEN 0 THEN 'Store Data'
			WHEN 1 THEN 'Never Share'
			WHEN 2 THEN 'Label Only'
			WHEN 3 THEN 'Shared Member'
			WHEN 4 THEN 'Dynamic Calc and Store'
			WHEN 5 THEN 'Dynamic' END AS DATA_STORAGE
		,SOLVE_ORDER
		,FORMULA
	FROM
		HSP_MEMBER_FORMULA mf
	LEFT JOIN
		(SELECT 0 AS PLAN_TYPE,'Default' AS TYPE_NAME UNION ALL 
		 SELECT PLAN_TYPE,TYPE_NAME FROM HSP_PLAN_TYPE) p ON mf.PLAN_TYPE = p.PLAN_TYPE
	WHERE
		p.TYPE_NAME = 'Plan1') mfp1 ON m.MEMBER_ID = mfp1.MEMBER_ID
LEFT JOIN
	(SELECT
		m.MEMBER_ID
		,amo.OBJECT_NAME AS ATTRIBUTE_NAME
	FROM
		HSP_MEMBER m
	INNER JOIN
		HSP_MEMBER_TO_ATTRIBUTE mta ON m.MEMBER_ID = mta.MEMBER_ID
	INNER JOIN
		HSP_OBJECT ado ON mta.ATTR_ID = ado.OBJECT_ID
	INNER JOIN
		HSP_OBJECT amo ON mta.ATTR_MEM_ID = amo.OBJECT_ID
	WHERE
		ado.OBJECT_NAME = 'AttrText') at ON m.MEMBER_ID = at.MEMBER_ID
LEFT JOIN
	(SELECT
		m.MEMBER_ID
		,amo.OBJECT_NAME AS ATTRIBUTE_NAME
	FROM
		HSP_MEMBER m
	INNER JOIN
		HSP_MEMBER_TO_ATTRIBUTE mta ON m.MEMBER_ID = mta.MEMBER_ID
	INNER JOIN
		HSP_OBJECT ado ON mta.ATTR_ID = ado.OBJECT_ID
	INNER JOIN
		HSP_OBJECT amo ON mta.ATTR_MEM_ID = amo.OBJECT_ID
	WHERE
		ado.OBJECT_NAME = 'AttrBoolean') ab ON m.MEMBER_ID = ab.MEMBER_ID
WHERE
	od.OBJECT_NAME = 'Product'

It makes for a long query…but effective.  This query will give us all of our properties for our first plan type.  And here are the results:

HSP_MEMBER_ALL_M

 

So SQL Server wasn’t too bad.  How about Oracle?  Well…that’s a different story.  There are two ways to approach this query on the Oracle side.  Option 1: combine all of the queries like we did in SQL Server.  Sounds easy enough until you remember back to Part 3 of our series where we first used the LISTAGG function.  That function makes this process a pain.  Instead of just combining the queries together, we have to add everything to our group by as well.  Here it is for fun:

SELECT
  o.OBJECT_NAME AS MEMBER_NAME
  ,op.OBJECT_NAME AS PARENT_NAME
  ,a1.ALIAS_NAME AS ALIAS_DEFAULT
	,a2.ALIAS_NAME AS ALIAS_ENGLISH
  ,LISTAGG(UDA_VALUE,', ') WITHIN GROUP (ORDER BY UDA_VALUE) "UDA LIST"
  ,CASE m.DATA_STORAGE
    WHEN 0 THEN 'Store Data'
    WHEN 1 THEN 'Never Share'
    WHEN 2 THEN 'Label Only'
    WHEN 3 THEN 'Shared Member'
    WHEN 4 THEN 'Dynamic Calc and Store'
    WHEN 5 THEN 'Dynamic'
    END AS DATA_STORAGE
  ,CASE m.DATA_TYPE
    WHEN 0 THEN 'Unspecified'
    WHEN 1 THEN 'Currency'
    WHEN 2 THEN 'Non-currency'
    WHEN 3 THEN 'Percentage'
    WHEN 4 THEN 'Enum'
    WHEN 5 THEN 'Date'
    WHEN 6 THEN 'Text'
    ELSE 'Unspecified'
    END AS DATA_TYPE
  ,CASE WHEN BITAND(m.USED_IN,1) = 1 THEN 
    CASE 
      WHEN BITAND(m.CONSOL_OP,6) = 6 THEN '^' 
      WHEN BITAND(m.CONSOL_OP,5) = 5 THEN '~' 
      WHEN BITAND(m.CONSOL_OP,4) = 4 THEN '%' 
      WHEN BITAND(m.CONSOL_OP,3) = 3 THEN '/' 
      WHEN BITAND(m.CONSOL_OP,2) = 2 THEN '*' 
      WHEN BITAND(m.CONSOL_OP,1) = 1 THEN '-' 
      ELSE '+' END
    ELSE
      NULL
    END AS PLAN1_CONSOL_OP
  ,dbms_lob.substr(CASE WHEN mfp1.FORMULA IS NULL THEN mfpd.FORMULA ELSE mfp1.FORMULA END,4000,1) AS PLAN1_FORMULA
	,CASE WHEN mfp1.DATA_STORAGE IS NULL THEN mfpd.DATA_STORAGE ELSE mfp1.DATA_STORAGE END AS PLAN1_DATA_STORAGE
	,CASE WHEN mfp1.SOLVE_ORDER IS NULL THEN mfpd.SOLVE_ORDER ELSE mfp1.SOLVE_ORDER END AS PLAN1_SOLVE_ORDER
FROM
	HSP_MEMBER m
INNER JOIN 
	HSP_OBJECT o ON m.MEMBER_ID = o.OBJECT_ID
INNER JOIN
	HSP_OBJECT op ON o.PARENT_ID = op.OBJECT_ID
INNER JOIN
	HSP_OBJECT od ON m.DIM_ID = od.OBJECT_ID
LEFT JOIN
	(	SELECT
			oa.OBJECT_NAME AS ALIAS_NAME
			,a.MEMBER_ID
		FROM
			HSP_ALIAS a
		INNER JOIN
			HSP_OBJECT oa ON a.ALIAS_ID = oa.OBJECT_ID
		INNER JOIN
			HSP_OBJECT oat ON a.ALIASTBL_ID = oat.OBJECT_ID
		WHERE
			oat.OBJECT_NAME = 'Default')
	 a1 ON m.MEMBER_ID = a1.MEMBER_ID
LEFT JOIN
	(	SELECT
			oa.OBJECT_NAME AS ALIAS_NAME
			,a.MEMBER_ID
		FROM
			HSP_ALIAS a
		INNER JOIN
			HSP_OBJECT oa ON a.ALIAS_ID = oa.OBJECT_ID
		INNER JOIN
			HSP_OBJECT oat ON a.ALIASTBL_ID = oat.OBJECT_ID
		WHERE
			oat.OBJECT_NAME = 'English')
	 a2 ON m.MEMBER_ID = a2.MEMBER_ID
LEFT JOIN
  ( SELECT 
      UDA_VALUE
      ,MEMBER_ID
    FROM
      HSP_MEMBER_TO_UDA mu
    INNER JOIN
      HSP_UDA u ON mu.UDA_ID = u.UDA_ID
    ) mu ON m.MEMBER_ID = mu.MEMBER_ID
LEFT JOIN
	(SELECT
		MEMBER_ID
		,mf.PLAN_TYPE
		,CASE DATA_STORAGE
			WHEN 0 THEN 'Store Data'
			WHEN 1 THEN 'Never Share'
			WHEN 2 THEN 'Label Only'
			WHEN 3 THEN 'Shared Member'
			WHEN 4 THEN 'Dynamic Calc and Store'
			WHEN 5 THEN 'Dynamic' END AS DATA_STORAGE
		,SOLVE_ORDER
		,FORMULA
	FROM
		HSP_MEMBER_FORMULA mf
	LEFT JOIN
		(SELECT 0 AS PLAN_TYPE,'Default' AS TYPE_NAME FROM DUAL UNION ALL 
		 SELECT PLAN_TYPE,TYPE_NAME FROM HSP_PLAN_TYPE) p ON mf.PLAN_TYPE = p.PLAN_TYPE
	WHERE
		p.TYPE_NAME = 'Default') mfpd ON m.MEMBER_ID = mfpd.MEMBER_ID
LEFT JOIN
	(SELECT
		MEMBER_ID
		,mf.PLAN_TYPE
		,CASE DATA_STORAGE
			WHEN 0 THEN 'Store Data'
			WHEN 1 THEN 'Never Share'
			WHEN 2 THEN 'Label Only'
			WHEN 3 THEN 'Shared Member'
			WHEN 4 THEN 'Dynamic Calc and Store'
			WHEN 5 THEN 'Dynamic' END AS DATA_STORAGE
		,SOLVE_ORDER
		,FORMULA
	FROM
		HSP_MEMBER_FORMULA mf
	LEFT JOIN
		(SELECT 0 AS PLAN_TYPE,'Default' AS TYPE_NAME FROM DUAL UNION ALL 
		 SELECT PLAN_TYPE,TYPE_NAME FROM HSP_PLAN_TYPE) p ON mf.PLAN_TYPE = p.PLAN_TYPE
	WHERE
		p.TYPE_NAME = 'Plan1') mfp1 ON m.MEMBER_ID = mfp1.MEMBER_ID
WHERE
	od.OBJECT_NAME = 'Product'
GROUP BY
  o.OBJECT_NAME
  ,op.OBJECT_NAME
  ,od.OBJECT_NAME
  ,a1.ALIAS_NAME
	,a2.ALIAS_NAME
  ,m.DATA_STORAGE
  ,m.DATA_TYPE
  ,CASE WHEN BITAND(m.USED_IN,1) = 1 THEN 
    CASE 
      WHEN BITAND(m.CONSOL_OP,6) = 6 THEN '^' 
      WHEN BITAND(m.CONSOL_OP,5) = 5 THEN '~' 
      WHEN BITAND(m.CONSOL_OP,4) = 4 THEN '%' 
      WHEN BITAND(m.CONSOL_OP,3) = 3 THEN '/' 
      WHEN BITAND(m.CONSOL_OP,2) = 2 THEN '*' 
      WHEN BITAND(m.CONSOL_OP,1) = 1 THEN '-' 
      ELSE '+' END
    ELSE
      NULL
    END
  ,dbms_lob.substr(CASE WHEN mfp1.FORMULA IS NULL THEN mfpd.FORMULA ELSE mfp1.FORMULA END,4000,1)
	,CASE WHEN mfp1.DATA_STORAGE IS NULL THEN mfpd.DATA_STORAGE ELSE mfp1.DATA_STORAGE END
	,CASE WHEN mfp1.SOLVE_ORDER IS NULL THEN mfpd.SOLVE_ORDER ELSE mfp1.SOLVE_ORDER END

It’s ugly, but it works. There was a problem with just adding everything to the GROUP BY.  Member formulas are of type CLOB which of course can not be used in a GROUP BY.  So what do we do?  We instead use a substring function to convert the CLOB to a 4,000 character VARCHAR.  This works great…assuming you don’t have any large formulas.  So this isn’t a great solution.

So what do we do?  Option B.  We take a step back and place our LISTAGG function into its own sub-query.  This let’s us move the group by to a very small place and still reference our member ID.  Here it is:

SELECT
  o.OBJECT_NAME AS MEMBER_NAME
  ,op.OBJECT_NAME AS PARENT_NAME
  ,a1.ALIAS_NAME AS ALIAS_DEFAULT
	,a2.ALIAS_NAME AS ALIAS_ENGLISH
  ,( SELECT LISTAGG(UDA_VALUE,', ') WITHIN GROUP (ORDER BY UDA_VALUE) "UDA_LIST" FROM (SELECT 
      UDA_VALUE
      ,MEMBER_ID
    FROM
      HSP_MEMBER_TO_UDA mu
    INNER JOIN
      HSP_UDA u ON mu.UDA_ID = u.UDA_ID) muda WHERE muda.MEMBER_ID = m.MEMBER_ID) AS UDA
  ,CASE m.DATA_STORAGE
    WHEN 0 THEN 'Store Data'
    WHEN 1 THEN 'Never Share'
    WHEN 2 THEN 'Label Only'
    WHEN 3 THEN 'Shared Member'
    WHEN 4 THEN 'Dynamic Calc and Store'
    WHEN 5 THEN 'Dynamic'
    END AS DATA_STORAGE
  ,CASE m.DATA_TYPE
    WHEN 0 THEN 'Unspecified'
    WHEN 1 THEN 'Currency'
    WHEN 2 THEN 'Non-currency'
    WHEN 3 THEN 'Percentage'
    WHEN 4 THEN 'Enum'
    WHEN 5 THEN 'Date'
    WHEN 6 THEN 'Text'
    ELSE 'Unspecified'
    END AS DATA_TYPE
  ,CASE WHEN BITAND(m.USED_IN,1) = 1 THEN 
    CASE 
      WHEN BITAND(m.CONSOL_OP,6) = 6 THEN '^' 
      WHEN BITAND(m.CONSOL_OP,5) = 5 THEN '~' 
      WHEN BITAND(m.CONSOL_OP,4) = 4 THEN '%' 
      WHEN BITAND(m.CONSOL_OP,3) = 3 THEN '/' 
      WHEN BITAND(m.CONSOL_OP,2) = 2 THEN '*' 
      WHEN BITAND(m.CONSOL_OP,1) = 1 THEN '-' 
      ELSE '+' END
    ELSE
      NULL
    END AS PLAN1_CONSOL_OP
  ,CASE WHEN mfp1.FORMULA IS NULL THEN mfpd.FORMULA ELSE mfp1.FORMULA END AS PLAN1_FORMULA
	,CASE WHEN mfp1.DATA_STORAGE IS NULL THEN mfpd.DATA_STORAGE ELSE mfp1.DATA_STORAGE END AS PLAN1_DATA_STORAGE
	,CASE WHEN mfp1.SOLVE_ORDER IS NULL THEN mfpd.SOLVE_ORDER ELSE mfp1.SOLVE_ORDER END AS PLAN1_SOLVE_ORDER
FROM
	HSP_MEMBER m
INNER JOIN 
	HSP_OBJECT o ON m.MEMBER_ID = o.OBJECT_ID
INNER JOIN
	HSP_OBJECT op ON o.PARENT_ID = op.OBJECT_ID
INNER JOIN
	HSP_OBJECT od ON m.DIM_ID = od.OBJECT_ID
LEFT JOIN
	(	SELECT
			oa.OBJECT_NAME AS ALIAS_NAME
			,a.MEMBER_ID
		FROM
			HSP_ALIAS a
		INNER JOIN
			HSP_OBJECT oa ON a.ALIAS_ID = oa.OBJECT_ID
		INNER JOIN
			HSP_OBJECT oat ON a.ALIASTBL_ID = oat.OBJECT_ID
		WHERE
			oat.OBJECT_NAME = 'Default')
	 a1 ON m.MEMBER_ID = a1.MEMBER_ID
LEFT JOIN
	(	SELECT
			oa.OBJECT_NAME AS ALIAS_NAME
			,a.MEMBER_ID
		FROM
			HSP_ALIAS a
		INNER JOIN
			HSP_OBJECT oa ON a.ALIAS_ID = oa.OBJECT_ID
		INNER JOIN
			HSP_OBJECT oat ON a.ALIASTBL_ID = oat.OBJECT_ID
		WHERE
			oat.OBJECT_NAME = 'English')
	 a2 ON m.MEMBER_ID = a2.MEMBER_ID
LEFT JOIN
	(SELECT
		MEMBER_ID
		,mf.PLAN_TYPE
		,CASE DATA_STORAGE
			WHEN 0 THEN 'Store Data'
			WHEN 1 THEN 'Never Share'
			WHEN 2 THEN 'Label Only'
			WHEN 3 THEN 'Shared Member'
			WHEN 4 THEN 'Dynamic Calc and Store'
			WHEN 5 THEN 'Dynamic' END AS DATA_STORAGE
		,SOLVE_ORDER
		,FORMULA
	FROM
		HSP_MEMBER_FORMULA mf
	LEFT JOIN
		(SELECT 0 AS PLAN_TYPE,'Default' AS TYPE_NAME FROM DUAL UNION ALL 
		 SELECT PLAN_TYPE,TYPE_NAME FROM HSP_PLAN_TYPE) p ON mf.PLAN_TYPE = p.PLAN_TYPE
	WHERE
		p.TYPE_NAME = 'Default') mfpd ON m.MEMBER_ID = mfpd.MEMBER_ID
LEFT JOIN
	(SELECT
		MEMBER_ID
		,mf.PLAN_TYPE
		,CASE DATA_STORAGE
			WHEN 0 THEN 'Store Data'
			WHEN 1 THEN 'Never Share'
			WHEN 2 THEN 'Label Only'
			WHEN 3 THEN 'Shared Member'
			WHEN 4 THEN 'Dynamic Calc and Store'
			WHEN 5 THEN 'Dynamic' END AS DATA_STORAGE
		,SOLVE_ORDER
		,FORMULA
	FROM
		HSP_MEMBER_FORMULA mf
	LEFT JOIN
		(SELECT 0 AS PLAN_TYPE,'Default' AS TYPE_NAME FROM DUAL UNION ALL 
		 SELECT PLAN_TYPE,TYPE_NAME FROM HSP_PLAN_TYPE) p ON mf.PLAN_TYPE = p.PLAN_TYPE
	WHERE
		p.TYPE_NAME = 'Plan1') mfp1 ON m.MEMBER_ID = mfp1.MEMBER_ID
WHERE
	od.OBJECT_NAME = 'Product'

This makes for a much shorter and less complex query.  Well, mildly less complex as this requires not just a sub-query, but a nested sub-query.  With either query, whether you choose the complex query with the big group by or the far simpler query with the nested sub-query, the results are the same:

HSP_MEMBER_ALL_O

And so the series on the HSP_MEMBER table (and all the other tables related) ends.  But wait, there are more properties missing?  The account dimension has more?  The entity dimension has more?  Tune in for yet another series, this time on the built-in dimensions.


The Planning Repository: HSP_MEMBER (Part 5 – Attribute Associations)

Not to sound like a broken record, but…a few disclaimers:

  • This is the fun stuff…that voids your warranty.  Not that you have to tell Oracle that you poke around here, but if you do, they will blame anything and everything that’s wrong on you for being in there.
  • The content here has been tested and validated against 11.1.2.3.500 and 11.1.2.4.
  • The content here has been tested and validated against Microsoft SQL Server and Oracle 11g.
  • The content here is based on the Vision sample application.
  • The content here is totally unsupported by Oracle (in case you missed the first bullet).

We are finally to the last piece before we put it all together: attribute associations.  As with UDA’s and formulas, we have another table to take a look at.  Members are associated with attribute members using the HSP_MEMBER_TO_ATTRIBUTE table.  This is a pretty simple table, but let’s still take a quick look at the structure:

Field NameSQL Server TypeOracle TypeDescription
MEMBER_IDintNUMBER(38,0)The OBJECT_ID of the member associated with the attribute member.
ATTR_IDintNUMBER(38,0)The OBJECT_ID of the attribute dimension.
ATTR_MEM_IDintNUMBER(38,0)The OBJECT_ID of the attribute dimension member associated with the member.
PERSPECTIVE1intNUMBER(38,0)I have not been able to figure out what this is used for. It seems to always be 0 in my testing.
PERSPECTIVE2intNUMBER(38,0)I have not been able to figure out what this is used for. It seems to always be 0 in my testing.

Pretty simple.  We have the member and the attribute member and also the attribute itself.  This means we will have to filter this table to get to specific attribute associations by attribute.  We also have two PERSPECTIVE fields that no longer appear to be in use.  So let’s do a simple query to take a look at associations for a specific attribute dimension:

SELECT
	o.OBJECT_NAME AS MEMBER_NAME
	,amo.OBJECT_NAME AS ATTRIBUTE_NAME
FROM
	HSP_MEMBER m
INNER JOIN
	HSP_OBJECT o ON m.MEMBER_ID = o.OBJECT_ID
INNER JOIN
	HSP_MEMBER_TO_ATTRIBUTE mta ON m.MEMBER_ID = mta.MEMBER_ID
INNER JOIN
	HSP_OBJECT ado ON mta.ATTR_ID = ado.OBJECT_ID
INNER JOIN
	HSP_OBJECT amo ON mta.ATTR_MEM_ID = amo.OBJECT_ID
WHERE ado.OBJECT_NAME = 'AttrText'

And here we see the results:

HSP_MEMBER_TO_ATTRIBUTE_1

So this is great to see all of the members that have a specific attribute associated, but how do we see all of the associations for all of our attribute dimensions?  We join to the above query for each of our attributes:

SELECT
    o.OBJECT_NAME AS MEMBER_NAME
	,at.ATTRIBUTE_NAME AS ATTRIBUTE_TEXT
	,ab.ATTRIBUTE_NAME AS ATTRIBUTE_BOOLEAN
FROM
	HSP_MEMBER m
INNER JOIN 
	HSP_OBJECT o ON m.MEMBER_ID = o.OBJECT_ID
INNER JOIN
	HSP_OBJECT od ON m.DIM_ID = od.OBJECT_ID
LEFT JOIN
	(SELECT
		m.MEMBER_ID
		,amo.OBJECT_NAME AS ATTRIBUTE_NAME
	FROM
		HSP_MEMBER m
	INNER JOIN
		HSP_MEMBER_TO_ATTRIBUTE mta ON m.MEMBER_ID = mta.MEMBER_ID
	INNER JOIN
		HSP_OBJECT ado ON mta.ATTR_ID = ado.OBJECT_ID
	INNER JOIN
		HSP_OBJECT amo ON mta.ATTR_MEM_ID = amo.OBJECT_ID
	WHERE
		ado.OBJECT_NAME = 'AttrText') at ON m.MEMBER_ID = at.MEMBER_ID
LEFT JOIN
	(SELECT
		m.MEMBER_ID
		,amo.OBJECT_NAME AS ATTRIBUTE_NAME
	FROM
		HSP_MEMBER m
	INNER JOIN
		HSP_MEMBER_TO_ATTRIBUTE mta ON m.MEMBER_ID = mta.MEMBER_ID
	INNER JOIN
		HSP_OBJECT ado ON mta.ATTR_ID = ado.OBJECT_ID
	INNER JOIN
		HSP_OBJECT amo ON mta.ATTR_MEM_ID = amo.OBJECT_ID
	WHERE
		ado.OBJECT_NAME = 'AttrBoolean') ab ON m.MEMBER_ID = ab.MEMBER_ID
WHERE
	od.OBJECT_NAME = 'Product'

This is just an example.  If I were to do this in a production environment, each of the left joins would be to views.  We would have a view for each of our attribute dimensions.  But for an example that you can easily execute, it works just fine.  Let’s look at the results:

HSP_MEMBER_TO_ATTRIBUTE_2

This gives us a list of all of our product dimension members along with the attribute member association.  And with that, we’re ready to put it all together and get a full extract of a custom dimension…in our next post.


The Planning Repository: Attribute Dimensions

Not to sound like a broken record, but…a few disclaimers:

  • This is the fun stuff…that voids your warranty.  Not that you have to tell Oracle that you poke around here, but if you do, they will blame anything and everything that’s wrong on you for being in there.
  • The content here has been tested and validated against 11.1.2.3.500 and 11.1.2.4.
  • The content here has been tested and validated against Microsoft SQL Server and Oracle 11g.
  • The content here is based on the Vision sample application.
  • The content here is totally unsupported by Oracle (in case you missed the first bullet).

Today we’ll take a brief break from the HSP_MEMBER related tables to lay the foundation for our next HSP_MEMBER post.  I think its important that we take look at our attribute dimensions before we see how they are associated with actual members.  There are two main tables that make up attribute dimension in the repository.  The first is HSP_ATTRIBUTE_DIM which contains the name, type, and associated dimension of our attribute.  The second is HSP_ATTRIBUTE_MEMBER which contains the members of the attribute dimension.  So let’s take our traditional look at the table structures for both.

HSP_ATTRIBUTE_DIM:

Field NameSQL Server TypeOracle TypeDescription
ATTR_IDintNUMBER(38,0)The OBJECT_ID of the attribute.
DIM_IDintNUMBER(38,0)The OBJECT_ID of the associated dimension for the attribute.
ATTR_TYPEsmallintNUMBER(38,0)The type of attribute dimension.
0 = Text
1 = Numeric
2 = Boolean
3 = Date
PERSPECTIVE1intNUMBER(38,0)No ideaÉmay not even ben in use. Let me know if you have a clue.
PERSPECTIVE2intNUMBER(38,0)No ideaÉmay not even ben in use. Let me know if you have a clue.

HSP_ATTRIBUTE_MEMBER:

Field NameSQL Server TypeOracle TypeDescription
ATTR_MEM_IDintNUMBER(38,0)The OBJECT_ID of the attribute dimension member.
ATTR_IDintNUMBER(38,0)The OBJECT_ID of the attribute dimension.
LEVEL0smallintNUMBER(38,0)This is supposed to tell us if the member is a leaf. It doesn't actually do that.
TEXT_VALvarchar(32)VARCHAR(32 CHAR)In theory this holds a text value for the member. Instead it is always null.
NUMBER_VALdecimal(18,0)NUMBER(38,0)In theory this holds a numeric value for the member. Again, always null.
BOOLEAN_VALsmallintNUMBER(38,0)There's a pattern forming here...always null.
DATE_VALdatetimeDATEYou guessed it...still null.

Obviously we have a few things to talk about.  Starting with the HSP_ATTRIBUTE_DIM, things are pretty straight forward.  We have a magic decoder ring for the attribute type and two columns that I still can’t actually identify.  The comments on the fields read “ID that identifies the 1st independent dimension”.  If you have any idea what that actually mean…drop me a line.  The good news is that it really doesn’t seem to matter.  Let’s have a look at the data:

SELECT
	ATTR_ID
	,o.OBJECT_NAME AS ATTR_NAME
	,DIM_ID
	,od.OBJECT_NAME AS DIM_NAME
	,ATTR_TYPE
	,PERSPECTIVE1
	,PERSPECTIVE2
FROM
	HSP_ATTRIBUTE_DIM ad
INNER JOIN
	HSP_OBJECT o ON ad.ATTR_ID = o.OBJECT_ID
INNER JOIN
	HSP_OBJECT od ON ad.DIM_ID = od.OBJECT_ID

This query should return a list of our attribute dimensions.  As I’m using the Vision sample application, I had to go through and add attribute dimensions.  We join twice to the HSP_OBJECT table to get the names of both the attribute dimension itself and the associated real dimension.  Here’s what it looks like with the joins:

HSP_ATTRIBUTE_DIM

How about the HSP_ATTRIBUTE_MEMBER table?  This one is more frustrating.  From all of my testing and checking, it appears that the only two columns of use are the first two.  After that, the columns either aren’t used (the value columns) or are not properly filled out (LEVEL0 is always 1).  Instead we ignore the value columns and just use the OBJECT_NAME and HAS_CHILDREN columns from our trusty HSP_OBJECT table.  So let’s take a look at a specific attribute dimension:

SELECT
	ATTR_MEM_ID
	,o.OBJECT_NAME AS ATTR_MEM_NAME
	,op.OBJECT_NAME AS ATTR_PARENT
	,ATTR_ID
	,oa.OBJECT_NAME AS ATTR_NAME
	,LEVEL0
	,o.HAS_CHILDREN
	,TEXT_VAL
	,NUMBER_VAL
	,BOOLEAN_VAL
	,DATE_VAL
FROM
	HSP_ATTRIBUTE_MEMBER am
INNER JOIN
	HSP_OBJECT o ON am.ATTR_MEM_ID = o.OBJECT_ID
INNER JOIN
	HSP_OBJECT oa ON am.ATTR_ID = oa.OBJECT_ID
INNER JOIN
	HSP_OBJECT op ON o.PARENT_ID = op.OBJECT_ID
WHERE
	oa.OBJECT_NAME = 'AttrText'
ORDER BY
	o.POSITION

This query should return a list of our attributes members.  Again, I had to add these, but there is a little bit of hierarchy to make sure that we understand which columns we should use to identify leaf-level members.  We have three joins to the HSP_OBJECT table this time.  First for the member name, then for the attribute dimension name, and finally for the name of our parent.  We also get the HAS_CHILDREN column from HSP_OBJECT table as the LEVEL0 column is always 1.  Perhaps when this table originated, the HSP_OBJECT table didn’t include HAS_CHILDREN.  So here’s the resulting data:

HSP_ATTRIBUTE_MEMBER

And that’s it for Attribute Dimensions.  We’ll dive into the relationship between members and attribute dimensions in our next post on the repository.


The Planning Repository: HSP_MEMBER (Part 4 – HSP_MEMBER_FORMULA)

Not to sound like a broken record, but…a few disclaimers:

  • This is the fun stuff…that voids your warranty.  Not that you have to tell Oracle that you poke around here, but if you do, they will blame anything and everything that’s wrong on you for being in there.
  • The content here has been tested and validated against 11.1.2.3.500 and 11.1.2.4.
  • The content here has been tested and validated against Microsoft SQL Server and Oracle 11g.
  • The content here is based on the Vision sample application.
  • The content here is totally unsupported by Oracle (in case you missed the first bullet).

As we near the end of the HSP_MEMBER series, we’ll take a look at a relatively low-complexity piece next:  member formulas.  As we mentioned before, formulas are stored in a separate table in Planning.  This is done because we can have a different formula for each of our plan types.  Our member formulas are stored in the HSP_MEMBER_FORMULA table.  Let’s take a look at the structure:

Field NameSQL Server TypeOracle TypeDescription
MEMBER_IDintNUMBER(38,0)The OBJECT_ID of the member.
PLAN_TYPEintNUMBER(38,0)The plan type.
DATA_STORAGEintNUMBER(38,0)The data storage type to be used in Essbase for the specific plan type.
SOLVE_ORDERintNUMBER(38,0)The plan type specific data storage for the member.
FORMULAntextCLOBThe member formula.

There’s not a lot of new information here.  The DATA_STORAGE field decodes just like it did in the HSP_MEMBER table and we just need to join to the HSP_PLAN_TYPE table to get our plan type names and to the HSP_OBJECT table to get our member names.  Let’s give it a go:

SQL Server:

SELECT
	MEMBER_ID
	,o.OBJECT_NAME AS MEMBER_NAME
	,mf.PLAN_TYPE
	,DATA_STORAGE
	,CASE DATA_STORAGE
		WHEN 0 THEN 'Store Data'
		WHEN 1 THEN 'Never Share'
		WHEN 2 THEN 'Label Only'
		WHEN 3 THEN 'Shared Member'
		WHEN 4 THEN 'Dynamic Calc and Store'
		WHEN 5 THEN 'Dynamic' END AS DATA_STORAGE
    ,SOLVE_ORDER
	,FORMULA
FROM
  HSP_MEMBER_FORMULA mf
INNER JOIN
	HSP_OBJECT o ON mf.MEMBER_ID = o.OBJECT_ID
LEFT JOIN
	(SELECT 0 AS PLAN_TYPE,'Default' AS TYPE_NAME UNION ALL 
	 SELECT PLAN_TYPE,TYPE_NAME FROM HSP_PLAN_TYPE) p ON mf.PLAN_TYPE = p.PLAN_TYPE 

Oracle:

SELECT
	MEMBER_ID
	,o.OBJECT_NAME AS MEMBER_NAME
	,mf.PLAN_TYPE
	,DATA_STORAGE
	,CASE DATA_STORAGE
		WHEN 0 THEN 'Store Data'
		WHEN 1 THEN 'Never Share'
		WHEN 2 THEN 'Label Only'
		WHEN 3 THEN 'Shared Member'
		WHEN 4 THEN 'Dynamic Calc and Store'
		WHEN 5 THEN 'Dynamic' END AS DATA_STORAGE
    ,SOLVE_ORDER
	,FORMULA
FROM
  HSP_MEMBER_FORMULA mf
INNER JOIN
	HSP_OBJECT o ON mf.MEMBER_ID = o.OBJECT_ID
LEFT JOIN
	(SELECT 0 AS PLAN_TYPE,'Default' AS TYPE_NAME FROM DUAL UNION ALL 
	 SELECT PLAN_TYPE,TYPE_NAME FROM HSP_PLAN_TYPE) p ON mf.PLAN_TYPE = p.PLAN_TYPE

Our goal here is to just read out all of the formulas for each plan type.  The most interesting thing here is that everything is stored by plan type.  Basically, this table tells us which formula to use for which cube inside of the Essbase outline.  The slight issue with this is that they also have the default calculation that exists in all used plan types unless another formula is specified for a plan type.  So we have a sub-query in our join that gives us the 0 plan type (default) that we then union to our standard plan type query.  Here are the results:

SQL Server (Management Studio):

HSP_MEMBER_FORMULA_M

Oracle (SQL Developer):

HSP_MEMBER_FORMULA_O

That gets us a list of all of the formulas for all of our members for all of our plan types and defaults.  What if I just want to figure out the formula for a specific plan type.  Because many developers just use the default formula, we have a little more logic that goes into it.  So we turn to a pair of sub-queries along with a few case statements:

SQL Server:

SELECT
	o.OBJECT_NAME AS MEMBER_NAME
	,CASE WHEN mfp1.FORMULA IS NULL THEN mfpd.FORMULA ELSE mfp1.FORMULA END AS PLAN1_FORMULA
	,CASE WHEN mfp1.DATA_STORAGE IS NULL THEN mfpd.DATA_STORAGE ELSE mfp1.DATA_STORAGE END AS PLAN1_DATA_STORAGE
	,CASE WHEN mfp1.SOLVE_ORDER IS NULL THEN mfpd.SOLVE_ORDER ELSE mfp1.SOLVE_ORDER END AS PLAN1_SOLVE_ORDER
FROM
	HSP_MEMBER m
INNER JOIN
	HSP_OBJECT o ON m.MEMBER_ID = o.OBJECT_ID
LEFT JOIN
	(SELECT
		MEMBER_ID
		,mf.PLAN_TYPE
		,CASE DATA_STORAGE
			WHEN 0 THEN 'Store Data'
			WHEN 1 THEN 'Never Share'
			WHEN 2 THEN 'Label Only'
			WHEN 3 THEN 'Shared Member'
			WHEN 4 THEN 'Dynamic Calc and Store'
			WHEN 5 THEN 'Dynamic' END AS DATA_STORAGE
		,SOLVE_ORDER
		,FORMULA
	FROM
		HSP_MEMBER_FORMULA mf
	LEFT JOIN
		(SELECT 0 AS PLAN_TYPE,'Default' AS TYPE_NAME UNION ALL 
		 SELECT PLAN_TYPE,TYPE_NAME FROM HSP_PLAN_TYPE) p ON mf.PLAN_TYPE = p.PLAN_TYPE
	WHERE
		p.TYPE_NAME = 'Default') mfpd ON m.MEMBER_ID = mfpd.MEMBER_ID
LEFT JOIN
	(SELECT
		MEMBER_ID
		,mf.PLAN_TYPE
		,CASE DATA_STORAGE
			WHEN 0 THEN 'Store Data'
			WHEN 1 THEN 'Never Share'
			WHEN 2 THEN 'Label Only'
			WHEN 3 THEN 'Shared Member'
			WHEN 4 THEN 'Dynamic Calc and Store'
			WHEN 5 THEN 'Dynamic' END AS DATA_STORAGE
		,SOLVE_ORDER
		,FORMULA
	FROM
		HSP_MEMBER_FORMULA mf
	LEFT JOIN
		(SELECT 0 AS PLAN_TYPE,'Default' AS TYPE_NAME UNION ALL 
		 SELECT PLAN_TYPE,TYPE_NAME FROM HSP_PLAN_TYPE) p ON mf.PLAN_TYPE = p.PLAN_TYPE
	WHERE
		p.TYPE_NAME = 'Plan1') mfp1 ON m.MEMBER_ID = mfp1.MEMBER_ID

Oracle:

SELECT
	o.OBJECT_NAME AS MEMBER_NAME
	,CASE WHEN mfp1.FORMULA IS NULL THEN mfpd.FORMULA ELSE mfp1.FORMULA END AS PLAN1_FORMULA
	,CASE WHEN mfp1.DATA_STORAGE IS NULL THEN mfpd.DATA_STORAGE ELSE mfp1.DATA_STORAGE END AS PLAN1_DATA_STORAGE
	,CASE WHEN mfp1.SOLVE_ORDER IS NULL THEN mfpd.SOLVE_ORDER ELSE mfp1.SOLVE_ORDER END AS PLAN1_SOLVE_ORDER
FROM
	HSP_MEMBER m
INNER JOIN
	HSP_OBJECT o ON m.MEMBER_ID = o.OBJECT_ID
LEFT JOIN
	(SELECT
		MEMBER_ID
		,mf.PLAN_TYPE
		,CASE DATA_STORAGE
			WHEN 0 THEN 'Store Data'
			WHEN 1 THEN 'Never Share'
			WHEN 2 THEN 'Label Only'
			WHEN 3 THEN 'Shared Member'
			WHEN 4 THEN 'Dynamic Calc and Store'
			WHEN 5 THEN 'Dynamic' END AS DATA_STORAGE
		,SOLVE_ORDER
		,FORMULA
	FROM
		HSP_MEMBER_FORMULA mf
	LEFT JOIN
		(SELECT 0 AS PLAN_TYPE,'Default' AS TYPE_NAME FROM DUAL UNION ALL 
		 SELECT PLAN_TYPE,TYPE_NAME FROM HSP_PLAN_TYPE) p ON mf.PLAN_TYPE = p.PLAN_TYPE
	WHERE
		p.TYPE_NAME = 'Default') mfpd ON m.MEMBER_ID = mfpd.MEMBER_ID
LEFT JOIN
	(SELECT
		MEMBER_ID
		,mf.PLAN_TYPE
		,CASE DATA_STORAGE
			WHEN 0 THEN 'Store Data'
			WHEN 1 THEN 'Never Share'
			WHEN 2 THEN 'Label Only'
			WHEN 3 THEN 'Shared Member'
			WHEN 4 THEN 'Dynamic Calc and Store'
			WHEN 5 THEN 'Dynamic' END AS DATA_STORAGE
		,SOLVE_ORDER
		,FORMULA
	FROM
		HSP_MEMBER_FORMULA mf
	LEFT JOIN
		(SELECT 0 AS PLAN_TYPE,'Default' AS TYPE_NAME FROM DUAL UNION ALL 
		 SELECT PLAN_TYPE,TYPE_NAME FROM HSP_PLAN_TYPE) p ON mf.PLAN_TYPE = p.PLAN_TYPE
	WHERE
		p.TYPE_NAME = 'Plan1') mfp1 ON m.MEMBER_ID = mfp1.MEMBER_ID

That’s a long query just to get member formulas!  On the bright side, it is far more usable than what the Outline Load Utility will produce.  And I’ve said this before when I’ve used sub-queries…I wouldn’t typically do this as a sub-query.  I would instead create views for these queries so that we can do regular joins and have a lot less maintenance should we need these formulas in other queries.  With that, let’s look at the results:

SQL Server (Management Studio):

HSP_MEMBER_FORMULA_2M

Oracle (SQL Developer):

HSP_MEMBER_FORMULA_2O

That’s where we’ll stop for now.  We have one last post to go (which will actually be two posts…) before we can put it all together and get one big query for each of our plan types with all of our properties.  Stay tuned for attribute associations.