MDX Crossjoin Region and Country and grand total - reporting-services

I need to join two hierarchies, Region and Country. From our Cube I have to get a result consisting of 1 region and 2 countries. This is what i've done so far:
SET [RegionSet] AS
{
[Stores].[Region].&[EUROPE]
}
SET [CountrySet] AS
{
[Stores].[Country].&[Japan]&[APAC]
,[Stores].[Country].&[China]&[APAC]
}
NON EMPTY
(
{
[RegionSet]
, [Stores].[Region].[All]
}
)
* (
{
[CountrySet]
, [Stores].[Country].[All]
}
)
ON ROWS
The MSD result is following:
But I want to list the region and countries in one column without the All entries and add a grand total to the last row. How can I do this?

I think if you had a multi-level user hierarchy called [Geography] as part of the [Stores] dimension then I think it would be trivial:
SET [RegionCountrySet] AS
{
[Stores].[Geography].[Region].&[EUROPE]
,[Stores].[Geography].[Country].&[Japan]&[APAC]
,[Stores].[Geography].[Country].&[China]&[APAC]
}
...but you can create custom aggregated members. Then use the created members on rows:
Pick a unused hierarchy to "host" your new members in. I will assume you have a [Languages] dimension. The new members are added as children to the [All] member of your chosen hierarchy.
Create tuples from the targets including the hosts [All] member in each of the tuples.
Like this
MEMBER [Languages].[Language].[All].[EUROPE] AS
(
[Languages].[Language].[All]
,[Stores].[Region].&[EUROPE]
)
MEMBER [Languages].[Language].[All].[Japan] AS
(
[Languages].[Language].[All]
,[Stores].[Country].&[Japan]&[APAC]
)
MEMBER [Languages].[Language].[All].[China] AS
(
[Languages].[Language].[All]
,[Stores].[Country].&[China]&[APAC]
)
SET [S1] AS
{
[Languages].[Language].[All].[EUROPE]
,[Languages].[Language].[All].[Japan]
,[Languages].[Language].[All].[China]
}
MEMBER [Languages].[Language].[All].[Grand Total] AS
Aggregate([S1])
SET [S2] AS
{
[Languages].[Language].[All].[EUROPE]
,[Languages].[Language].[All].[Japan]
,[Languages].[Language].[All].[China]
,[Languages].[Language].[All].[Grand Total]
}
Then in the script try this
[S2] ON ROWS

Related

Filter Table, based on field in another table

I'm stumped with this. I have a table with various fields:
$employees. This, I guess, is what you call a collection, I think, that when I call, returns all employee records in the database (4 records in this example)
Each employee record has the following fields
first_name, last_name, age, other_id
There is another table (or collection), which I'm calling filter table. It is called $other_ids. This has two records, with the following fields - id, id_name.
I want to be able to filter the $employees table so that it only keeps the records, where other_id is equal to one of the two values of id in the filter table- $other_ids
So for example, if the filter table has the following two records:
[{"id":1 "id_name":"one"}, {"id":2, "id_name":"two"}]
And the $employee table contains the records:
[{"first_name":"ted", "surname_name":"stark", "age":35, "other_id":1},
{"first_name":"fred", "surname_name":"strange", "age":30, "other_id":2},
{"first_name":"incredible", "surname_name":"hulk", "age":25, "other_id":3},
{"first_name":"captain", "surname_name":"stone", "age":28, "other_id":2}]
After, the filtering, it should return $employees_filtered should only have records 1, 2, and 4
I've tried doing left-join and using whereHas, and where clauses, but nothing works!
I think you are looking for something like -
$otherId = [1, 2];
$employees_filtered = Employee::with('Others')->whereIn('other_id', $otherId)->get();
Please don't forget to make a relationship with their model.
In Other.php model -
public function Employees()
{
return $this->hasMany('App\Other', 'other_id', 'id');
}
And in Employee.php model -
public function Others()
{
return $this->belongsTo('App\Employee', 'other_id', 'id');
}

mySQL JSON : search array of objects where property value in list

I have a JSON column, manifest, containing an array of objects.
I need to return all table rows where any of the objects in their array have a slide_id that is present in a sub select.
The structure of the JSON field is..
{ matrix:[
{
row:1,
col:1,
slide_id:1
},
{
row:1,
col:2,
slide_id:5
}
]
}
So I want to run something like this....
SELECT id FROM presentation WHERE manifest->'$.matrix[*].slide_id' IN ( (SELECT id from slides WHERE date_deleted IS NOT NULL) );
But this doesn't work as manifest->'$.matrix[*].slide_id' returns a JSON array for each row.
I have managed to get this to work, but its amazingly slow as it scans the whole table...
SELECT
p.id
FROM
(
SELECT id,
manifest->'$.matrix[*].slide_id' as slide_ids
FROM `presentation`
) p
INNER JOIN `pp_slides` s
ON JSON_CONTAINS(p.slide_ids, CAST(s.id as json), '$')
WHERE s.date_deleted IS NOT NULL
If I filter it down to an individual presentation ID, then its not too bad, but still takes 700 ms for a presentation with a couple of hundred slides in it. Is there a cleaner way to do this?
I suppose the best way would be to refactor it to store the matrix as a relational table....

mysql Order By tree depth

I have table 'pe' with columns id, name, and lcltyid
I have table 'wp_exrz_locality_localities' with id, name, and parent
the locality table is a tree and the parent contains an id of another locality row. pe.lcltyid is a key wp_exrz_locality_localities.id
basically what I want to do is retrieve all 'pe' entries sorted by their "tree depth"
However the total depth of the tree can be any amount at any time. And I need the depth in a way that allows me to use it in a sub query for sorting.
Originally I thought I needed a stored proceedure/function to get the depth of a lclty entry. After i made the proceedure I found out that proceedures cant be used in expressions. Then I tried to make a function but binary logging is enabled by me host and "log_bin_trust_function_creators = 0", so no stored functions for me.
Lastly I am trying to understand recursion but can 't seem to make it work. I am just trying to create a recursive statement that will retrieve the "depth" meaning the number of parents for an individual node up until the top node, or when the parent = 0 I just get an error "syntax to use near 'RECURSIVE node_ancestors..."
WITH RECURSIVE node_ancestors(id, parent) AS (
SELECT id, id FROM `wp_exrz_locality_localities` WHERE id IN (1, 2, 3)
UNION ALL
SELECT na.id, wp_exrz_locality_localities.parent
FROM node_ancestors AS na, wp_exrz_locality_localities
WHERE wp_exrz_locality_localities.id = na.parent AND wp_exrz_locality_localities.parent != 0
)
SELECT id, COUNT(parent) AS depth FROM node_ancestors GROUP BY id;
Any help is greatly appreciated
an example:
EDIT
table pe:
id---name---lcltyid
2---first---4
3---second---3
table wp_exrz_locality_localities:
id---name---parent
1---USA---0
3---SanFran---1
4---California---3
SELECT * FROM 'pe' ORDER BY ([lcltydepth]) ASC;
desired output:
id---name---lcltyid
3---second---3
2---first---4
where lclctydepth is 3 for the "first" pe and 2 for "second" because the second one is attached to a state with only the US above it and the first one is attached to a city, with state and US above it. So it would order them by the number of parents required to get the the last parent with parentid = 0;
I hope this helps?

How To Filter a list in String with MDX

Goal:
Filter the value in the table column based on string "Canada" as a criteria.
Problem:
I cannot get the MDX syntax code in where state to be correct in order to filter the data based on Canada.
Information:
This situation is a simplified sample and my request is to add filter criteria in the where state.
Data source is SSAS:s AdventureWorksDW2012
Code:
SELECT
{
[Measures].[Reseller Order Count],
[Measures].[Discount Amount]
} ON COLUMNS,
{
([Reseller].[Reseller Type].[Business Type].ALLMEMBERS )
} ON ROWS
FROM [Adventure Works]
WHERE
(
FILTER
(
[Geography].[Country].ALLMEMBERS, [Geography].[Country].NAME ='Canada'
)
)
There are two issues:
In Adventure Works, the Geography dimension is not linked to the Internet Sales measure group. Have a look at the cube definition, tab "Dimension Usage". You should use the hierarchies from the Customer dimension found in the Location folder when using measures from the Internet Sales measure group. I use [Customer].[Country] below.
In a filter, you should use Current and a set alias to refer to the current element during the iteration of the set.
The following shows what you want, I just changed the Filter:
SELECT
{
[Measures].[Reseller Order Count],
[Measures].[Discount Amount]
} ON COLUMNS,
{
([Reseller].[Reseller Type].[Business Type].ALLMEMBERS )
} ON ROWS
FROM [Adventure Works]
WHERE
(
FILTER
(
[Customer].[Country].ALLMEMBERS as c, c.current.NAME ='Canada'
)
)

SQL issue: one to many relationship and EAV model

Good evening guys,
I'm a newbie to web programming and I need your help to solve a problem inherent to SQL query.
The database engine I'm using is MySQL and I access it via PHP, here I'll explain a simplified version of my database, just to fix ideas.
Let's suppose to work with a database containing three tables: teams, teams_information, attributes. More precisely:
1) teams is a table containing some basic information about italian football teams (soccer, not american football :D), it is formed by three fields: 'id' (int, primary key), 'name' (varchar, team name), nickname (Varchar, team nickname);
2) attributes is a table containing a list of possible information about a football team, such as city (the city where team plays its home match), captain (team captain's fullname), f_number (number of fans) and so on. This table is formed by three fields: id (int, primary key), attribute_name (varchar, an identifier for the attribute), attribute_desc (text, an explanation of the meaning of attribute). Each record of this table represents a single possible attribute of a football team;
3) teams_information is a table where some information, about teams listed in team table, are available. This table contains three fields: id (int, primary key), team_id (int, a foreign key which identifies a team), attribute_id (int, a foreign key which identifies one of the attributes listed in attributes table), attribute_value (varchar, the value of the attribute). Each record represents a single attribute of a single team. In general, different teams will have a different number of information, so for some teams a large number of attributes will be available while for other teams only a small number of attributes will be available.
Note that relation between teams and teams_information is one to many and the same relation exists between attributes and teams_information
Well, given this model my purpose is to realize a grid (maybe with ExtJS 4.1) to show user the list of italian football team, each record of this grid will represent a single football team and will contain all possible attributes: some fields may be empty (because, for considered team, the correspondent attribute is unknown), while the others will contain the values stored in teams_information table (for the considered team).
According to the above grid's field are: id, team_name and a number of fields to represent all the different attributes listed in 'attributes' table.
My question is: can I realize such a grid by using a SINGLE SQL query (maybe a proper SELECT query, to fetch all data I need from database tables) ?
Can anyone suggest me how to write a similar query (if it exists) ?
Thanks in advance for helping me.
Regards.
Enrico.
The short answer to your question is no, there is no simple construct in MySQL to achieve the result set you are looking for.
But it is possible to carefully (painstakingly) craft such a query. Here is an example, I trust you will be able to decipher it. Basically, I'm using correlated subqueries in the select list, for each attribute I want returned.
SELECT t.id
, t.name
, t.nickname
, ( SELECT v1.attribute_value
FROM team_information v1
JOIN attributes a1
ON a1.id = v1.attribute_id AND a1.attribute_name = 'city'
WHERE v1.team_id = t.id ORDER BY 1 LIMIT 1
) AS city
, ( SELECT v2.attribute_value
FROM team_information v2 JOIN attributes a2
ON a2.id = v2.attribute_id AND a2.attribute_name = 'captain'
WHERE v2.team_id = t.id ORDER BY 1 LIMIT 1
) AS captain
, ( SELECT v3.attribute_value
FROM team_information v3 JOIN attributes a3
ON a3.id = v3.attribute_id AND a3.attribute_name = 'f_number'
WHERE v3.team_id = t.id ORDER BY 1 LIMIT 1
) AS f_number
FROM teams t
ORDER BY t.id
For 'multi-valued' attributes, you'd have to pull each instance of the attribute separately. (Use the LIMIT to specify whether you are retrieving the first one, the second one, etc.)
, ( SELECT v4.attribute_value
FROM team_information v4 JOIN attributes a4
ON a4.id = v4.attribute_id AND a4.attribute_name = 'nickname'
WHERE v4.team_id = t.id ORDER BY 1 LIMIT 0,1
) AS nickname_1st
, ( SELECT v5.attribute_value
FROM team_information v5 JOIN attributes a5
ON a5.id = v5.attribute_id AND a5.attribute_name = 'nickname'
WHERE v5.team_id = t.id ORDER BY 1 LIMIT 1,1
) AS nickname_2nd
, ( SELECT v6.attribute_value
FROM team_information v6 JOIN attributes a6
ON a6.id = v6.attribute_id AND a6.attribute_name = 'nickname'
WHERE v6.team_id = t.id ORDER BY 1 LIMIT 2,1
) AS nickname_3rd
I use nickname as an example here, because American soccer clubs frequently have more than one nickname, e.g. Chicago Fire Soccer Club has nicknames: 'The Fire', 'La Máquina Roja', 'Men in Red', 'CF97', et al.)
NOT AN ANSWER TO YOUR QUESTION, BUT ...
Have I mentioned numerous times before, how much I dislike working with EAV database implementations? What should IMO be a very simple query turns into an overly complicated beast of a potentially light dimming query.
Wouldn't it be much simpler to create a table where each "attribute" is a separate column? Then queries to return reasonable result sets would look more reasonable...
SELECT id, name, nickname, city, captain, f_number, ... FROM team
But what really makes me shudder is the prospect that some developer is going to decide that the LDQ should be "hidden" in the database as a view, to enable the "simpler" query.
If you go this route, PLEASE PLEASE PLEASE resist any urge you may have to store this query in the database as a view.
I'm going to take a slightly different route. Spencer's answer is fantastic, and it addresses the issue quite well, but there's still a large underlying problem.
The data that you are trying to display on the site is over-normalized in the database. I won't elaborate, since, again, Spencer's answer highlights the issue pretty well.
Rather, I'd like to recommend a solution that denormalizes the data a bit.
Convert all of your Team data into a single table with many columns. (If there is Player data that isn't covered in the question, that would be a second table, but I'll gloss over that for now.)
Sure, you'll have a whole bunch of columns, and a lot of the columns might be NULL for a lot of the rows. It's not normalized, and it's not pretty, but here's the huge advantage that you gain.
Your query becomes:
SELECT * FROM Teams
That's it. That gets displayed right to the website and you are done. You might have to go out of your way to realize this schema, but it would be totally worth the time investment.
I think what you're saying is that you want the rows in the attributes table to appear as columns in the result recordset. If this is correct, then then in SQL you would use PIVOT.
A quick search on SO seems to indicate that there is no PIVOT equivalent in MySql.
I wrote a simple PHP script to generalize spencer's idea to solve my issue.
Here's the code:
<?php
require_once('includes/db.config.php'); //this file performs connection to mysql
/*
* Following function requires a table name ($table)
* and a number of service fields ($num). Given those parameters
* it returns the number of table fields (excluding service fields).
*/
function get_fields_number($table,$num,$conn)
{
$query = "SELECT * FROM $table";
$result = mysql_query($query,$conn);
return mysql_num_fields($result)-$num; //remember there are $num service fields
}
/*
* Following function requires a table name ($table) and an array
* containing a list of service fields names. Given those parameters,
* it returns the list of field names. That list is contained within an array and
* service fields are excluded.
*/
function get_fields_name($table,$service,$conn)
{
$query = "SELECT * FROM $table";
$result = mysql_query($query,$conn);
$name = array(); //Array to be returned
for ($i=0;$i<mysql_num_fields($result);$i++)
{
if(!in_array(mysql_field_name($result,$i),$service))
{
//currently selected field is not a service field
$name[] = mysql_field_name($result,$i);
}
}
return $name;
}
//Below $conn is db connection created in 'db.config.php'
$query = "SELECT `name` FROM `detail_arg` WHERE visibility = 0";
$res = mysql_query($query,$conn);
if($res===false)
{
$err_msg = mysql_real_escape_string(mysql_error($conn));
echo "{success:false,data:'".$err_msg."'}";
die();
}
$arg = array(); //list of argument names
while($row = mysql_fetch_assoc($res))
{
$arg[] = $row['name'];
}
//Following function writes the select subquery which is
//necessary to build a column containing a single attribute.
function make_subquery($attribute) //$attribute contains attribute name
{
$query = "";
$query.="(SELECT incident_detail.arg_value ";
$query.="FROM incident_detail ";
$query.="INNER JOIN detail_arg ";
$query.="ON incident_detail.arg_id = detail_arg.id AND detail_arg.name='".$attribute."' ";
$query.="WHERE incident.id = incident_detail.incident_id) ";
$query.="AS $attribute";
return $query;
}
/*
echo make_subquery("date"); //debug code
*/
$subquery = array(); //list of subqueries
for($i=0;$i<count($arg);$i++)
{
$subquery[] = make_subquery($arg[$i]);
}
$query = "SELECT "; //final query containing subqueries
$fields = get_fields_name("incident",array("id","visibility"),$conn);
//list of 'incident' table's fields
for($i=0;$i<count($fields);$i++)
{
$query.="incident.".$fields[$i].", ";
}
//insert the subqueries
$sub = implode($subquery,", ");
$query .= $sub;
$query.=" FROM incident ORDER BY incident.id";
echo $query;
?>