Thursday, February 17, 2011

SQL queries for Essbase developers

So when I first set up this blog I posted that I would not only write about Essbase all of the time and I would include other content as well. Looking back over my posts, I have not lived up to that too well, so here's one in the "other technology" category. Of course it still has an Essbase slant on it, but even if you are one of those non-believers that uses a different OLAP technology, some of these examples might still be of interest to you.

Recursive (Parent/Child) queries in SQL.
Let's say you are well organized and have your metadata structures in relational tables, rather than just living in some Excel files. Now suppose you want to spin off a cube from one of these structures, but you don't want the entire structure, maybe you just want a particular branch of the hierarchy (i.e. all descendants below a certain parent). Pulling that kind of query in a relational database used to be kind of tough. Here's an example of how to do it.

Let's say you start off with an Outline extract of the Sample Basic database for the Market dimension and you want to query the table to get all descendant of "East".


WITH Descendants (child,parent) AS
(SELECT [CHILD0,Market], [PARENT0,Market]
FROM dbo.Market
WHERE [PARENT0,Market] = 'East'
UNION ALL
SELECT dbo.Market.[CHILD0,Market],
dbo.Market.[PARENT0,Market]
FROM dbo.Market
INNER JOIN Descendants ON dbo.Market.[PARENT0,Market]= Descendants.child)

SELECT DISTINCT dbo.Market.[PARENT0,Market], dbo.Market.[CHILD0,Market]
FROM dbo.Market
INNER JOIN Descendants ON dbo.Market.[CHILD0,Market] = Descendants.child




Your results will be


PARENT0,Market CHILD0,Market
East Connecticut
East Florida
East Massachusetts
East New Hampshire
East New York



Play around with it and see what you can do with it.
Be forewarned, if the table is big this might take a while.
Also note that this "Descendants" feature in MS SQL Server didn't show up until SQL Server 2005.


Another one I have heard is how do I take a parent/child table and turn it into Generations or Levels.

Using a basic Parent/Child table going to Generations would look like this


SELECT a_1.Parent AS Gen1, b_1.Child AS Gen2, c_1.Child AS Gen3, d_1.Child AS Gen4, e_1.Child AS Gen5
FROM (SELECT Parent
FROM dbo.Test_table
WHERE (Parent NOT IN
(SELECT DISTINCT Child
FROM dbo.Test_table AS A))) AS a_1 INNER JOIN
(SELECT DISTINCT Child, Parent
FROM dbo.Test_table AS B) AS b_1 ON a_1.Parent = b_1.Parent LEFT OUTER JOIN
(SELECT DISTINCT Child, Parent
FROM dbo.Test_table AS C) AS c_1 LEFT OUTER JOIN
(SELECT DISTINCT Child, Parent
FROM dbo.Test_table AS D) AS d_1 LEFT OUTER JOIN
(SELECT DISTINCT Child, Parent
FROM dbo.Test_table AS E) AS e_1 ON d_1.Child = e_1.Parent ON c_1.Child = d_1.Parent ON b_1.Child = c_1.Parent
GROUP BY a_1.Parent, b_1.Child, c_1.Child, d_1.Child, e_1.Child



If you want to go to levels it would look like this.


SELECT a_1.Child AS lev0, b_1.Parent AS lev1, c_1.Parent AS lev2,
d_1.Parent AS lev3, e_1.Parent AS lev4
FROM (
SELECT Child
FROM dbo.Test_table
WHERE(Child NOT IN
(SELECT DISTINCT Parent
FROM dbo.Test_table AS A))) AS a_1 INNER JOIN
(SELECT DISTINCT Child, Parent
FROM dbo.Test_table AS B) AS b_1 ON a_1.Child = b_1.Child LEFT OUTER JOIN
(SELECT DISTINCT Child, Parent
FROM dbo.Test_table AS C) AS c_1 LEFT OUTER JOIN
(SELECT DISTINCT Child, Parent
FROM dbo.Test_table AS D) AS d_1 LEFT OUTER JOIN
(SELECT DISTINCT Child, Parent
FROM dbo.Test_table AS E) AS e_1 ON d_1.Parent = e_1.Child ON
c_1.Parent = d_1.Child ON b_1.Parent = c_1.Child



Lastly, let's say you have Generations and you want to go to Parent/Child.
It is important to have a sort key on your source table to maintain the proper order.

So assume the source table looks like

Sort_ID, Gen1, Gen2, Gen3, Gen4

You would create a target table then run some insert queries to cycle through the source table. Lastly I like to run a couple of update queries to tag lev0 members and upper level members.


CREATE TABLE [dbo].[Target] (
[ID] [decimal](18, 0) IDENTITY (1, 1) NOT NULL ,
[parent] [varchar] (50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
[member] [varchar] (50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
[group_item_flag] [varchar] (1) COLLATE SQL_Latin1_General_CP1_CI_AS NULL
) ON [PRIMARY]


INSERT INTO [dbo].[Target] ([parent], [member])
SELECT [Gen1], [Gen2]
FROM [dbo].[Source]
GROUP BY [Gen1], [Gen2]
HAVING (NOT ([Gen2] IS NULL))
ORDER BY MIN([sort_key])

INSERT INTO [dbo].[Target] ([parent], [member])
SELECT [Gen2], [Gen3]
FROM [dbo.[Source]
GROUP BY [Gen2], [Gen3]
HAVING (NOT ([Gen3] IS NULL))
ORDER BY MIN([sort_key])

INSERT INTO [dbo].[Target] ([parent], [member])
SELECT [Gen3], [Gen4]
FROM [dbo.[Source]
GROUP BY [Gen1], [Gen2]
HAVING (NOT ([Gen2] IS NULL))
ORDER BY MIN([sort_key])

UPDATE [dbo].[Target]
SET [group_item_flag] = 'G'
WHERE [member] IN (
SELECT DISTINCT [parent]
FROM [dbo].[Target])

UPDATE [dbo].[Target]
SET [group_item_flag] = 'I'
WHERE [member] NOT IN (
SELECT DISTINCT [parent]
FROM [dbo].[Target])



Let me know some of your favorites in the comments.

2 comments:

christi parks said...

Hello all,I am new and I would like to ask that what are the benefits of sql training, what all topics should be covered and it is kinda bothering me ... and has anyone studies from this course wiziq.com/course/125-comprehensive-introduction-to-sql of SQL tutorial online?? or tell me any other guidance...
would really appreciate help... and Also i would like to thank for all the information you are providing on sql.

Oracle SQL said...

I lately came across your weblog and have been learning along. I believed I would leave my 1st comment. I do not know what to say except that I've loved reading. Fine blog. I will keep visiting this weblog incredibly normally.