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:.
Brian Marshall
February 1, 2016
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:
Next up…our motherboard.
Brian Marshall
January 25, 2016
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
Brian Marshall
January 17, 2016
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:
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:

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

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

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?
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.
Brian Marshall
January 13, 2016
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!
Brian Marshall
January 11, 2016
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:

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:

And the script results:

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?

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:

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
Brian Marshall
November 9, 2015
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:

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:

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.
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 Name | SQL Server Type | Oracle Type | Description |
MEMBER_ID | int | NUMBER(38,0) | The OBJECT_ID of the member associated with the attribute member. |
ATTR_ID | int | NUMBER(38,0) | The OBJECT_ID of the attribute dimension. |
ATTR_MEM_ID | int | NUMBER(38,0) | The OBJECT_ID of the attribute dimension member associated with the member. |
PERSPECTIVE1 | int | NUMBER(38,0) | I have not been able to figure out what this is used for. It seems to always be 0 in my testing. |
PERSPECTIVE2 | int | NUMBER(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:

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:

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.
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 Name | SQL Server Type | Oracle Type | Description |
ATTR_ID | int | NUMBER(38,0) | The OBJECT_ID of the attribute. |
DIM_ID | int | NUMBER(38,0) | The OBJECT_ID of the associated dimension for the attribute. |
ATTR_TYPE | smallint | NUMBER(38,0) | The type of attribute dimension.
0 = Text
1 = Numeric
2 = Boolean
3 = Date |
PERSPECTIVE1 | int | NUMBER(38,0) | No ideaÉmay not even ben in use. Let me know if you have a clue. |
PERSPECTIVE2 | int | NUMBER(38,0) | No ideaÉmay not even ben in use. Let me know if you have a clue. |
HSP_ATTRIBUTE_MEMBER:
Field Name | SQL Server Type | Oracle Type | Description |
ATTR_MEM_ID | int | NUMBER(38,0) | The OBJECT_ID of the attribute dimension member. |
ATTR_ID | int | NUMBER(38,0) | The OBJECT_ID of the attribute dimension. |
LEVEL0 | smallint | NUMBER(38,0) | This is supposed to tell us if the member is a leaf. It doesn't actually do that. |
TEXT_VAL | varchar(32) | VARCHAR(32 CHAR) | In theory this holds a text value for the member. Instead it is always null. |
NUMBER_VAL | decimal(18,0) | NUMBER(38,0) | In theory this holds a numeric value for the member. Again, always null. |
BOOLEAN_VAL | smallint | NUMBER(38,0) | There's a pattern forming here...always null. |
DATE_VAL | datetime | DATE | You 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:

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:

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.
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 Name | SQL Server Type | Oracle Type | Description |
MEMBER_ID | int | NUMBER(38,0) | The OBJECT_ID of the member. |
PLAN_TYPE | int | NUMBER(38,0) | The plan type. |
DATA_STORAGE | int | NUMBER(38,0) | The data storage type to be used in Essbase for the specific plan type. |
SOLVE_ORDER | int | NUMBER(38,0) | The plan type specific data storage for the member. |
FORMULA | ntext | CLOB | The 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):

Oracle (SQL Developer):

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):

Oracle (SQL Developer):

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.