MDX Scope for all attributes of a dimension - ssas

I have a dimension X with 10 attributes 1..10. In order to make a custom calculation for measure Y within a scope I can write this statement:
SCOPE([X].[1].CHILDREN, [Measures].[Y]);
THIS = 1+1;
END SCOPE;
This will make sure that when dimension attribute 1 is selected together with Y then the result will be 2.
Is there any easy way of writing this to make sure it works for all attributes within dimension X instead of using a nested scope and explicitly defining it for all attributes?
Something like SCOPE([X].[*]) or similar?

Create one more calculated member like this:
CREATE MEMBER DimSelected AS
IIF(
[X].[1].CURRENTMEMBER IS [X].[1].[All]
AND
[X].[2].CURRENTMEMBER IS [X].[2].[All]
AND
[X].[3].CURRENTMEMBER IS [X].[3].[All]
AND
[X].[4].CURRENTMEMBER IS [X].[4].[All]
AND
[X].[5].CURRENTMEMBER IS [X].[5].[All]
AND
[X].[6].CURRENTMEMBER IS [X].[6].[All]
AND
[X].[7].CURRENTMEMBER IS [X].[7].[All]
AND
[X].[8].CURRENTMEMBER IS [X].[8].[All]
AND
[X].[9].CURRENTMEMBER IS [X].[9].[All]
AND
[X].[10].CURRENTMEMBER IS [X].[10].[All],
NULL,
1)
And modify your scope logic to below:
SCOPE([Measures].[Y]);
IF Measures.DimSelected = 1 THEN This = 1+1 END IF;
END SCOPE;
Alternatively if you just plan to ever select the dim on some given axis, below might work:
CREATE MEMBER MembersSelected AS
GENERATE(AXIS(1) AS a, a.current.item(0).unique_name)
CREATE member HasX as
IIF(instr(1,MembersSelected,"[X].") <> 0, "Yes", NULL)
Then modify your scope logic to below:
SCOPE([Measures].[Y]);
IF Measures.HasX = "Yes" THEN This = 1+1 END IF;
END SCOPE;

Related

Is possible do Select * form DynamicValue to perform a query like this in Navision?

Is possible do Select * form DynamicValue to perform a query like this in Navision?
Thanks in advance
To perform a sql query li this select * from DynamicValue You must do something like this.
Imagine that you have a table and you are going to show the data in a page or form.
Variables:
RecDynamicValue (Table).
PagDynamicValues (Page).
Code:
RecDynamicValue.RESET; //Clean filters
CLEAR(PagDynamicValues);
PagDynamicValues.SETTABLEVIEW(RecDynamicValue); //Set RecDynamicValue (Table)
PagDynamicValues.RUN; (Open Page)
In this code when page is open you can see al records from DynamicValue table like a Select * from DynamicValue.
If you need perform a loop for all records from DynamicValue table in code try this:
RecDynamicValue.RESET;
IF RecDynamicValue.FINDSET THEN REPEAT //Repeat clausule for a loop
//Loop...
//Loop...
//Loop...
UNTIL RecDynamicValue.NEXT = 0; //Repeat until last value
In all cases first you need declare a variable, SubType = Record and specified ID or name of record.
You can not change the value of the table variable by code.
But perhaps you can use RecordRef function to do that.
For example:
RecRef.OPEN(27); //Id of ItemTable
RecRef.FINDFIRST;
FldRef := RecRef.FIELD(3); // Item.Description;
FldRef.VALUE('New description');
RecRef.MODIFY;
In your case your DynamicValue is parameter to RecRef.OPEN("Your Dynamic Value") here you need specified value ID of your table.
You can also perform a loop using RecorRef.

Displaying SSAS measure in thousands or millions

I have an Olap cube created using Microsoft SSAS. Inside I have a many-to-many relationship between source transaction currency and required "Reporting" currency. This is all functional, however to display a dynamic currency symbol I am using the "Currency" format string default and passing in a custom LCID based on the currency selected.
The problem with using the "Currency" format is the decimal places and large numbers. I am reporting millions of pounds/dollars and my CFO wants to see these numbers reported in thousands or millions. To control this I have read about using a special format string like #,, but this won't allow the currency symbol to be shown.
I had an idea to have a special dimension which would equate to 1, 1000, 1000000 and then create a calculated measure which divides by this (obviously defaulting to 1 and not aggregatable), but I have lots of measures.
Can anybody else advise on an alternative approach?
I would just set the FORMAT_STRING via a script assignment:
FORMAT_STRING(([Dim-Currency].[Currency Code].&[USD])) = "$#,,";
FORMAT_STRING(([Dim-Currency].[Currency Code].&[Euro])) = "€#,,";
You may use the SCOPE statement here:
Scope(AddCalculatedMembers([Measures].Members));
This = case
when [Measures].CurrentMember >= 1000000
then Cstr(Cint([Measures].CurrentMember / 1000000)) + " millions"
when [Measures].CurrentMember >= 1000
then Cstr(Cint([Measures].CurrentMember / 1000)) + " thousands"
else [Measures].CurrentMember
end;
End Scope;
Where Cint return int value and Cstr helps to join int value to text. I'm not sure if it's not too much. I've never used the "Currency" type, honestly.
I'm with #GregGalloway. In our cube-script it is implemented like this:
Scope
([Dim-Currency].[Currency Code].&[EUR]);
//EUR
FORMAT_STRING(This) = '€ #,##0.00';
End Scope;
Scope
([Dim-Currency].[Currency Code].&[GBP]);
FORMAT_STRING(This) = '£ #,##0.00';
End Scope;
We render via Pyramid front-end.

mdx - how to replace null values with '0' in measures members

In my MDX query I'm using this set of measures in my SELECT statement:
With SET [Selected Measures] AS {
[Measures].[CTR],
[Measures].[Cost],
[Measures].[Clicks]
}
I want in my result to replace the NULL values in '0'.
How to do this?
Try re-defining your measures:
With member [Measures].[Not Null CTR] as Iif( IsEmpty( [Measures].[CTR] ), 0, [Measures].[CTR] )
...
Select { [Measures].[Not Null CTR], ...} on Columns
...
Perhaps changing the measure name for something nicer or renaming the columns back to the original names on the client.
EDIT: If you want to keep the names and the names output by your client are just CTR, etc. (without brackets or the Measures prefix), and you have an extra dimension available somewhere (one that is not used anywhere else in the query), you can define those new members in that extra dimension:
With member [My Other Dim].[CTR] as Iif( IsEmpty( [Measures].[CTR] ), 0, [Measures].[CTR] )
...
Select { [My Other Dim].[CTR], ...} on rows,
...
Definitely not an elegant solution, but it works.
Is it possible to touch upon the cube design? If so you need the open the cube solution, navigate to the "Calculations" tab and add in the below code. Then deploy the changes.
SCOPE([Measures].[CTR]);
IF THIS IS NULL THEN this = 0 END IF;
END SCOPE;
SCOPE([Measures].[Cost]);
IF THIS IS NULL THEN this = 0 END IF;
END SCOPE;
SCOPE([Measures].[Clicks]);
IF THIS IS NULL THEN this = 0 END IF;
END SCOPE;
Based on the first answer, here is an approach that work for the all the measures in the cube :
SCOPE([Measures].MEMBERS);
THIS = IIF(IsEmpty([Measures].CurrentMember) , 0, [Measures].CurrentMember);
END SCOPE;
hope it helps.

MDX CurrentMember with SSAS 2008 doesn't work as stated by MSDN

First of all I use the SQL Management Studio for this query (no Excel 2007 that seems to have problems):
WITH
SET [Project period dates] AS
{
StrToMember("[Time].[Date].&[" + [Project].[ParentProject].CURRENTMEMBER.PROPERTIES("Project Start Iso") + "]"):
StrToMember("[Time].[Date].&[" + [Project].[ParentProject].CURRENTMEMBER.PROPERTIES("Project End Iso") + "]")
}
MEMBER [Measures].[Test] AS ([Project period dates].COUNT)
SELECT
{
[Measures].[Test]
}
on 0,
NONEMPTY ([Project].[ParentProject].MEMBERS)
DIMENSION PROPERTIES [Project].[ParentProject].[Project Duration], [Project].[ParentProject].[Project Start Iso], [Project].[ParentProject].[Project End Iso]
on 1
FROM
[MyCube]
WHERE
(
[Orgunit].[Orgunit].&[448]
)
This query delivers a list of projects with its three properties and a calculated member that is based upon my calculated set. The properties show the right values, but the calculated member shows always the same: the result of the very first project it should be calculated for.
I don't really understand why, because MSDN says:
The current member changes on a hierarchy used on an axis in a query.
Therefore, the current member on other hierarchies on the same
dimension that are not used on an axis can also change; this behavior
is called 'auto-exists'.
They give examples with calculated members, but I think that should also work with calculated sets, I have read that query-based calculated sets are dynamic by nature. Maybe somebody can tell me if I understood that wrong or what else is my problem here.
The named set are only computed once within a query. That is why your calculated member always return the same value.
You just have to remove the named set from your query:
MEMBER [Measures].[Test] AS {
StrToMember("[Time].[Date].&[" + [Project].[ParentProject].CURRENTMEMBER.PROPERTIES("Project Start Iso") + "]"):
StrToMember("[Time].[Date].&[" + [Project].[ParentProject].CURRENTMEMBER.PROPERTIES("Project End Iso") + "]")
}.COUNT

Rails: batched attribute queries using AREL

I'd like to use something like find_in_batches, but instead of grouping fully instantiated AR objects, I would like to group a certain attribute, like, let's say, the id. So, basically, a mixture of using find_in_batches and pluck:
Cars.where(:engine => "Turbo").pluck(:id).find_in_batches do |ids|
puts ids
end
# [1, 2, 3....]
# ...
Is there a way to do this (maybe with Arel) without having to write the OFFSET/LIMIT logic myself or recurring to pagination gems like will paginate or kaminari?
This is not the ideal solution, but here's a method that just copy-pastes most of find_in_batches but yields a relation instead of an array of records (untested) - just monkey-patch it into Relation :
def in_batches( options = {} )
relation = self
unless arel.orders.blank? && arel.taken.blank?
ActiveRecord::Base.logger.warn("Scoped order and limit are ignored, it's forced to be batch order and batch size")
end
if (finder_options = options.except(:start, :batch_size)).present?
raise "You can't specify an order, it's forced to be #{batch_order}" if options[:order].present?
raise "You can't specify a limit, it's forced to be the batch_size" if options[:limit].present?
relation = apply_finder_options(finder_options)
end
start = options.delete(:start)
batch_size = options.delete(:batch_size) || 1000
relation = relation.reorder(batch_order).limit(batch_size)
relation = start ? relation.where(table[primary_key].gteq(start)) : relation
while ( size = relation.size ) > 0
yield relation
break if size < batch_size
primary_key_offset = relation.last.id
if primary_key_offset
relation = relation.where(table[primary_key].gt(primary_key_offset))
else
raise "Primary key not included in the custom select clause"
end
end
end
With this, you should be able to do :
Cars.where(:engine => "Turbo").in_batches do |relation|
relation.pluck(:id)
end
this is not the best implementation possible (especially in regard to primary_key_offset calculation, which instantiates a record), but you get the spirit.