MDX - Visual totals on user hierarchy - ssas

I'd like to have a visual total on a user hierarchy in MDX
That's to say :
I've got a team T1,
with people P1,P2,P3.
Each of these people has got a yearly objective O1,O2,O3
at certain date in this year, only P2 has worked.
By filtering the people on a other measure called "Profit Potential".
I see
Team Hierachy | Objective
--------------+-------------
"T1 - (all)" | O1 + O2 +O3
"T1 - P2 " | O2
"T2 - (all) | O4
(snip) | (snap)
Since P1 and P3 wind up not involved at all, what I want to achieve is
Team Hierachy | Objective
--------------+-------------
"T1 - (all)" | O2
"T1 - P2 " | O2
"T2 - (all) | O4
(snip) | (snap)
Thanks in advance.

There are two solutions to enable visual totals :
First method is to use the MDX VisualTotals (http://msdn.microsoft.com/en-us/library/ms145527.aspx).
OR
The second method is to use a role and check the "Enable visual totals" checkbox (http://blogs.microsoft.co.il/blogs/barbaro/archive/2008/02/06/visual-totals-in-mdx-and-role-security.aspx).
Since you do not want to apply security, you must go with the MDX approach.

Related

How to create two JOIN-tables so that I can compare attributes within?

I take a Database course in which we have listings of AirBnBs and need to be able to do some SQL queries in the Relationship-Model we made from the data, but I struggle with one in particular :
I have two tables that we are interested in, Billing and Amenities. The first one have the id and price of listings, the second have id and wifi (let's say, to simplify, that it equals 1 if there is Wifi, 0 otherwise). Both have other attributes that we don't really care about here.
So the query is, "What is the difference in the average price of listings with and without Wifi ?"
My idea was to build to JOIN-tables, one with listings that have wifi, the other without, and compare them easily :
SELECT avg(B.price - A.price) as averagePrice
FROM (
SELECT Billing.price, Billing.id
FROM Billing
INNER JOIN Amenities
ON Billing.id = Amenities.id
WHERE Amenities.wifi = 0
) A, (
SELECT Billing.price, Billing.id
FROM Billing
INNER JOIN Amenities
ON Billing.id = Amenities.id
WHERE Amenities.wifi = 1) B
WHERE A.id = B.id;
Obviously this doesn't work... I am pretty sure that there is a far easier solution to it tho, what do I miss ?
(And by the way, is there a way to compute the absolute between the difference of price ?)
I hope that I was clear enough, thank you for your time !
Edit : As mentionned in the comments, forgot to say that, but both tables have idas their primary key, so that there is one row per listing.
Just use conditional aggregation:
SELECT AVG(CASE WHEN a.wifi = 0 THEN b.price END) as avg_no_wifi,
AVG(CASE WHEN a.wifi = 1 THEN b.price END) as avg_wifi
FROM Billing b JOIN
Amenities a
ON b.id = a.id
WHERE a.wifi IN (0, 1);
You can use a - if you want the difference instead of the specific values.
Let's assume we're working with data like the following (problems with your data model are noted below):
Billing
+------------+---------+
| listing_id | price |
+------------+---------+
| 1 | 1500.00 |
| 2 | 1700.00 |
| 3 | 1800.00 |
| 4 | 1900.00 |
+------------+---------+
Amenities
+------------+------+
| listing_id | wifi |
+------------+------+
| 1 | 1 |
| 2 | 1 |
| 3 | 0 |
+------------+------+
Notice that I changed "id" to "listing_id" to make it clear what it was (using "id" as an attribute name is problematic anyways). Also, note that one listing doesn't have an entry in the Amenities table. Depending on your data, that may or may not be a concern (again, refer to the bottom for a discussion of your data model).
Based on this data, your averages should be as follows:
Listings with wifi average $1600 (Listings 1 and 2)
Listings without wifi (just 3) average 1800).
So the difference would be $200.
To achieve this result in SQL, it may be helpful to first get the average cost per amenity (whether wifi is offered). This would be obtained with the following query:
SELECT
Amenities.wifi AS has_wifi,
AVG(Billing.price) AS avg_cost
FROM Billing
INNER JOIN Amenities ON
Amenities.listing_id = Billing.listing_id
GROUP BY Amenities.wifi
which gives you the following results:
+----------+-----------------------+
| has_wifi | avg_cost |
+----------+-----------------------+
| 0 | 1800.0000000000000000 |
| 1 | 1600.0000000000000000 |
+----------+-----------------------+
So far so good. So now we need to calculate the difference between these 2 rows. There are a number of different ways to do this, but one is to use a CASE expression to make one of the values negative, and then simply take the SUM of the result (note that I'm using a CTE, but you can also use a sub-query):
WITH
avg_by_wifi(has_wifi, avg_cost) AS
(
SELECT Amenities.wifi, AVG(Billing.price)
FROM Billing
INNER JOIN Amenities ON
Amenities.listing_id = Billing.listing_id
GROUP BY Amenities.wifi
)
SELECT
ABS(SUM
(
CASE
WHEN has_wifi = 1 THEN avg_cost
ELSE -1 * avg_cost
END
))
FROM avg_by_wifi
which gives us the expected value of 200.
Now regarding your data model:
If both your Billing and Amenities table only have 1 row for each listing, it makes sense to combine them into 1 table. For example: Listings(listing_id, price, wifi)
However, this is still problematic, because you probably have a bunch of other amenities you want to model (pool, sauna, etc.) So you might want to model a many-to-many relationship between listings and amenities using an intermediate table:
Listings(listing_id, price)
Amenities(amenity_id, amenity_name)
ListingsAmenities(listing_id, amenity_id)
This way, you could list multiple amenities for a given listing without having to add additional columns. It also becomes easy to store additional information about an amenity: What's the wifi password? How deep is the pool? etc.
Of course, using this model makes your original query (difference in average cost of listings by wifi) a bit tricker, but definitely still doable.

Designing a SSAS Cube with many to many reference table.

I have a table with the following schema.
Dim LocationProductsMapping Table (the key in this table is not used anywhere. It is just a primary key column. )
Key Location Products
XX A1 P1
XX A2 P2
XX A3 P3
XX A1 P2
XX A3 P2
* Dim Products (Say P1 , P2 , P3 as keys)
* Dim SellingMode (Say S1 , S2 , S3 as keys)
* Dim Shop (Shop1,Shop10,Shop100)
Fact Sales table
Product SellingMode Shop Revenue
P1 S1 Shop1 $100
P1 S2 Shop10 $400
P1 S1 Shop100 $100
P1 S3 Shop1 $100
P2 S2 Shop10 $400
P1 S1 Shop100 $100
P3 S3 Shop1 $100
Now I need to build a CUBE.
How should I create a dimension which should include My Location products mapping? (i.e, when I filter by Location, I should only get the following data of the location ).
Output
SellingMode1 $2000 Revenue 20 number of Products
SellingMode2 $3000 Revenue 25 number of products
I tried to create a hierarchy in Dimension like Location,ProductKey. But that doens't help. Values are not proper and filter condition is not applied.
I cannot change the table schema
LocationProductsMapping table is not included in the Datasource view automatically. ( I added this )
I don’t have the Dimension created for “LocationProductsMapping “, (understandable as this is the reference table)
I am not sure if you have a separate location table. If you have one, build a dimension from it, otherwise use the Location column from the LocationProductsMapping table to build the dimension.
Then create a new measure group from your LocationProductsMapping table. As Analysis Services cannot have measure groups without measures, use the count which the wizard normally suggests. Make this measure invisible, as it is not useful for users. Then on the "Dimension Usage" tab of cube designer, make sure your mapping measure group is related to the Product and Location dimensions, and set the relationship from the main measure group to location to "Many-to-many", selecting the mapping measure group.
And you are done. Analysis Services handles the rest for you.

Semi-hierarchical SQL query with multiple tables and possible outer joins

I have products. Each product is made up of items and assemblies. Assemblies themselves can be made up of items too. So it's a hierarchy but limited in depth. What I would like to do is list products with the items and assemblies it contains, plus any items in the product's assemblies.
This is the output I would like to see. It doesn't have to look exactly like this, but the aim is to show the items in the product, then the assemblies and within each assembly the items with in it. The number of columns isn't fixed, if more are necessary to show the items in the assemblies there is no problem with that.
ProductID ProductName AssemblyID AssemblyName ItemID ItemName
--------- ----------- ---------- ------------ ------ --------
P0001001 Product One
I0045 Item A
I0082 Item B
A00023 Assembly 1
I0320 Item 1
I0900 Item 2
A00024 Assembly 2
I0877 Item 3
I0900 Item 2
I0042 Item 4
This I can then use to build a report grouped on the Product ID to list the contents of each product.
This is the table structure I have at the moment.
+ProductList-+ +ProductItems-+
|ProductID | ----------> |ProductID | +ItemList-+
|ProductName | \ |ItemID | --------------------------------> |ItemID |
|Price | \ +-------------+ > |ItemName |
+------------+ \ / |Cost |
\ +ProductAssemblies-+ / +---------+
\-> |ProductID | +AssemblyItems-+ /
+-- |AssemblyID | ----> |AssemblyID | /
| |BuildTime | |ItemID | ---/
| +------------------+ +--------------+
|
| +AssemblyList-+
+-> |AssemblyID |
|AssemblyName |
+-------------+
What kind of SELECT statement would I need to do this.
I think I need some sort of outer join but I'm not totally up on SQL syntax to know how to structure the select statement. All my efforts have always led to the product being listed multiple times for each item and assembly. So if a product has 3 items and 2 assemblies, the product appears with 6 times.
Searching for this kind of problem is not easy as I don't know what I need to search on. Is it a three table problem, an outer join issue, or just a simple syntactical answer.
Or would it be better to switch to a pure hierarchical table structure without the use of assemblies? It would then be easier to search on hierarchical tables to solve any problems I might have.
I'm using LibreOffice 3.5.6.2 Base. It has wizards and other helpful things but they don't extend to the complexity of the situation that I find myself in. The aim is that the database contains prices and it can be used to properly price out the products from the cost of the items and time to build the assemblies.
Be gentle, I'm a newbie to SO.
The normal SQL approach to this would put all the data on one line, rather than split among several lines. So, your data would look like:
ProductID ProductName AssemblyID AssemblyName ItemID ItemName
--------- ----------- ---------- ------------ ------ --------
P0001001 Product One I0045 Item A
P0001001 Product One I0045 Item B
P0001001 Product One A00023 Assembly 1 I0320 Item 1
P0001001 Product One A00023 Assembly 1 I0320 Item 2
. . .
The product and assembly information, for instance, would not be blank for a given item. All would be on the same line.
This information comes from two sources, the product items and the assembly items. The following query gets each component, then unions them together, finally ordering the results by product:
select *
from ((select p.Productid, p.ProductName, NULL as AssemblyId, NULL as AssemblyName, il.Itemid, il.ItemName
from Product p join
ProductItems pi
on p.productId = pi.ProductId join
ItemList il
on pi.ItemId = il.ItemId
) union all
(select p.Productid, p.ProductName, al.AssemblyId, al.AssemblyName, il.Itemid, il.ItemName
from Product p join
ProductAssemblies pa
on pa.ProductId = p.ProductId join
AssemblyList al
on pl.AssembyId = al.AssemblyId, join
AssemblyItems ai
on al.AssemblyItems join
ItemList il
on p.ItemId = il.ItemId
)
) t
order by 1, 2, 3, 4, 5, 6
Often, restructuring into the format you want would be done at the app level. You can do it in SQL, but the best approach depends on the database you are using.

SSRS report to display Outlook type calendar - People x Days x Activities

I am after a pattern/view/opinion/tip/weblink from an SSRS/SQL expert on how I might create a report that enables me to list something like the following:
[----Person----] | [29-Sep-11] | [30-Sep-11] | [01-Oct-11] | [02-Oct-11] | [03-Oct-11]... and so on...
Bob Bobertson | Activity A | Activity B | ----Empty--- | Activity C | ---Empty--- |
Rob Robertson | Activity D | Activity E | Activity F | Activity G | ---Empty--- |
...and so on...
Date columns are dynamic - example, 10 days on from today (rolling window report)
Person column is dynamic list based on a user collection
So the above table looks pretty simple, but theres an extra dimension/depth to it.
I need to get the details for the Activity to create a link to it and style it in the report based on other flags.
I'm currently stumped on how to structure my resultset, and then how to Group/Pivot the data into a report structure.
Has anyone done similar before?
I'm using CRM4.0 for records including Date, Person, ActivityTitle, Billable etc
SSRS 2008 for the report building via VS2008 BI studio
Turns out to be very simple! You can just insert a tablix and chose the date field for the column headers, and the Username for the row labels. Add a formula for the intersect to use more than one of the remaining values, and thats the three dimensions solved! – BennIT
Adolf Garlic:
Think of the tablix (matrix) control as a "design time pivot table" and you can't go wrong
Many thanks for your input Adolf!

SQL:finding duplicate entries within a group (with a variable number of elements)

I have a table of groups (GROUPS) defined by the group_id (PK) and some other fields.
Each group may consist of a variable number of elements and their values. This group composition is stored in a second table (GROUP_COMPOSITION) that has a PK field (counter),a field for the group_id, a field for the element name and a field for the value of the element name.
For example:
Table of groups:
groupId
g1
g2
Table of Group composition:
PK groupID Element_Name Element_Value
1 g1 Material A
2 g1 Temperature 37
3 g2 Color white
4 g2 Temperature 50
5 g2 Material B
6 g3 Material C
7 g4 Color Red
So, if trying to insert a "new group" (g5) exclusively defined by Material=B and Color=white and Temperature =50, i would like to identify it as a repeated group (g2).
I would like to prevent duplicate "group composition" insertion in the second table, where group composition is defined by the total number of elements and their values.
I am thinking of a INTERSECT query for all posible elements to be inserted, but not sure whether this is the optimal way.
This is related to the post SQL to find duplicate entries (within a group), but in this case, the criteria to find duplicate insertion was based on the number of elements (not on their nature).
I would really apprettiate any help
Thanks
I think I would build a function and go the intersection route. I guess you could also build a string to associate each group with a value representing all the group data. Then apply the same function to your candidate group and check for a match on the elements. Here is a Postgresql example of a possible aggegation:
SELECT g.groupid, array_to_string(g.element_array, ',') elements
FROM (SELECT o.groupid, array_agg(o.element_name ||'='|| o.element_value) AS element_array
FROM (SELECT groupid, element_name, element_value
FROM composition
ORDER BY 1, 2) o
GROUP BY groupid) g
ORDER BY groupid
groupid | elements
---------+---------------------------------------
g1 | Material=A,Temperature=37
g2 | Color=white,Material=B,Temperature=50
g3 | Material=C
g4 | Color=Red
The inner ordering is to ensure it generates consistently. Seems Oracle 11gR2 has a LISTAGG function for string concatenation which could be useful. Or you could build your own aggregate function to do this. If this data is relatively static, you might want to pre-compute and store on insert rather than regenerate with each query.
Since your data seems to indicate there is a fairly stable range of properties to these groups, why not make them first-class citizens instead and promote them to columns?
groupid | Material | Temperature | Color
--------+----------+-------------+-----------
g1 | A | 37 | <null>
g2 | B | 50 | White
g3 | C | <null> | <null>
g4 | <null> | <null> | Red
It wouldn't be too hard to dynamically add columns as needed if you were so inclined, but I'm a little curious as to how you want to define a group as "repeated"? Whenever the attributes of a new group has at least one corresponding atribute in the existing group? If so, the query would be really simple:
select groupid from table where
Material=B and
Color=White
If there are any returned rows, then you already have a group with at least those properties.