I'm new to BI and MDX and have a need to do this. I have a dimension that looks like this:
<Dimension name="JobDim">
<Hierarchy name="Job" hasAll="true" primaryKey="jobID">
<Table name="JobDim" schema="dw"/>
<Level name="Job Code" column="jobCode" type="String" uniqueMembers="false"/>
</Hierarchy>
<Hierarchy name="Job Status" hasAll="true" primaryKey="jobID">
<Table name="JobDim" schema="dw"/>
<Level name="Job Status" column="status" type="String" hideMemberIf="IfBlankName"/>
</Hierarchy>
</Dimension>
I have a calculated measure and I need to set the value of this measure depending on the 'job status', so if the job is 'complete', set the measure to 100, otherwise set its value to 200 (the value to be used is more complex but for this example purpose, this is good enough). I'm using something like this:
<CalculatedMember name="Earned Revenue" dimension="Measures" formatString="$#,###.00;($#,###.00)" aggregator="sum">
<Formula>case [JobDim.Job Status].CurrentMember
when [JobDim.Job Status].[Complete] then 100
else 200
end
</Formula>
</CalculatedMember>
I've tried different variations of the code above, but none seem to work (some even cause a crash when I try to run the report). Anyone has done something like this in the past that can provide me an answer?
You need to evaluate that tuple against some measure. Remember that in MDX evaluations are done against the level/aggregate results, not on a row by row basis.
From your description it looks like Job Status should be a property of the level Job Code, and not a level.
Try the following on your schema:
<Dimension name="JobDim">
<Hierarchy name="Job" hasAll="true" primaryKey="jobID">
<Table name="JobDim" schema="dw"/>
<Level name="Job Code" column="jobCode" type="String" uniqueMembers="false">
<Property name="Job Status" column="status" type="String"/>
</Level>
</Hierarchy>
</Dimension>
and on your calculated measure
<CalculatedMember name="Earned Revenue" dimension="Measures" formatString="$#,###.00;($#,###.00)" aggregator="sum">
<Formula>case [JobDim.Job Status].CurrentMember.Properties("Job Status")
when "Complete" then 100
else 200
end
</Formula>
</CalculatedMember>
Better to use IIF than CASE if possible as it is generally quicker. Assuming [JobDim.Job Status].[Complete] is a member in your cube then the IS operator should be ok to use:
IIF(
[JobDim.Job Status].CurrentMember
IS [JobDim.Job Status].[Complete]
,100
,200
)
Related
I have an ASPX.VB script page to drive the selection of filter parameters for the web page - https://maldivescomplete.com/maldivesv/Room%20Types/RoomTypeSearch.aspx
Filtering on "Rating" works perfectly, but filtering on "Room Size" (constructed in exactly the same way) only works for the first two options an finds no records for the other 3.
Hoping I missing something terribly obvious that a more insightful ASP/VB person can spot quickly?
Select the range
<asp:DropDownList ID="RoomSize" runat="server">
<asp:ListItem Value="20000000">Any</asp:ListItem>
<asp:ListItem Value="100">Less than 100</asp:ListItem>
<asp:ListItem Value="300">100 - 300</asp:ListItem>
<asp:ListItem Value="500">300 - 500</asp:ListItem>
<asp:ListItem Value="2000">More than 500</asp:ListItem>
</asp:DropDownList>
Identify the bottom number of the range with ASP.VB code to select the comparison numbers from the menu selections
If Rating.SelectedIndex = 0 Or Rating.SelectedIndex = 1 Then
e.Command.Parameters("Rating2").Value = 0
Else
e.Command.Parameters("Rating2").Value = Rating.Items(Rating.SelectedIndex - 1).Value
End If
...
If RoomSize.SelectedIndex = 0 Or RoomSize.SelectedIndex = 1 Then
e.Command.Parameters("RoomSize2").Value = 0
Else
e.Command.Parameters("RoomSize2").Value = RoomSize.Items(RoomSize.SelectedIndex - 1).Value
End If
Set control parameters
<asp:ControlParameter ControlID="RoomSize" Name="CPRoomSize"
PropertyName="SelectedValue" Type="Int32" />
<asp:Parameter Name="RoomSize2" Type="Int32" />
Run filter Boolean (below part of larger expression obviously
#CPRoomSize > [Square Metres] AND #RoomSize2 <= [Square Meters]
I've tried a number of different variations to explore the behaviour, but I can't get the one filter to behave like the other. All other aspects are the same (data type of the field, etc).
I'm trying to rewrite the following cts query using XML structure query:
cts:element-pair-geospatial-query(
fn:QName("http://www.example.com/2009/foo","wgs84"),
fn:QName("http://www.example.com/2009/foo","latitude"),
fn:QName("http://www.example.com/2009/foo","longitude"),
cts:circle("#12 53.411541,-2.9900994"),
("coordinate-system=wgs84","score-function=reciprocal","slope-factor=4"),
32
)
I converted it into:
<geo-elem-pair-query>
<parent ns="http://www.example.com/2009/foo" name="wgs84" />
<lat ns="http://www.example.com/2009/foo" name="latitude" />
<lon ns="http://www.example.com/2009/foo" name="longitude" />
<fragment-scope>documents</fragment-scope>
<geo-option>coordinate-system=wgs84</geo-option>
<geo-option>score-function=reciprocal</geo-option>
<geo-option>slope-factor=4</geo-option>
<circle>
<radius>12.0</radius>
<point>
<latitude>53.411541</latitude>
<longitude>-2.9900994</longitude>
</point>
</circle>
</geo-elem-pair-query>
Unfortunately, I don't know how to add weight into <geo-elem-pair-query>. Accordingly to MarkLogic documentation it seems to be unsupported (but cts equivalent supports it). I've tried to add <weight>32.0</weight> but it doesn't work.
Do you know if there is a way to add weight to geo-elem-pair-query structure query?
How to set-up a model in icCube to allow to drill down to the details, when details contain text fields?
The idea is to get a list, with column names containing the text fields (in combination with amount fields). Just like a simple SQL statement would give.
I have tried the following:
a) added a technical dimension that is linked to the rows (via rownumber) and added MIN Aggregation for the text fields. With the idea to use these when a DRILLTHROUGH MDX statement is invoked. The DRILLTHROUGH function works, but does not give the values next to each other for the measures. Result is like:
b) added each unique line a line number and loaded the line number as lowest detail in one of the dimensions. Added attributes for these text and date items for the "drillthrough" columns. Next, added calculated measures to get the property for these attributes. The drillthrough is now effectively a drillby to the lowest details. It works, but this is not nice as it blows up my dimension.
c) tried to use the widget data source SQL, but it is not available for text files, and it does not work for MSAccess files (too slow).
The preferable solution should works in the dashboards and in any XMLA/REST API interface.
Enclosed this example
the schema file
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<schemaFactory revisionNumber="7">
<schemaDefinition name="drilltrhough-text" description="" group="Issues" loadOnStartup="false">
<activateIncrementalLoad>false</activateIncrementalLoad>
<useUnknownMembersInFacts>true</useUnknownMembersInFacts>
<autoCleanUpTableColumns>false</autoCleanUpTableColumns>
<useFactPartitioning>false</useFactPartitioning>
<callGarbageCollector>NONE</callGarbageCollector>
<backup>NONE</backup>
<nonEmptyCachePolicy>NONE</nonEmptyCachePolicy>
<nonEmptyCacheType>REGULAR</nonEmptyCacheType>
<nonEmptyCachePersistency>MEMORY</nonEmptyCachePersistency>
<storagePolicy>DEFAULT</storagePolicy>
<hierarchyUniqueNameStyle>IncludeDimensionName</hierarchyUniqueNameStyle>
<inMemoryDS name="data">
<memoryDataTable tableName="data" rowLimit="-1" id="d9429713-9be8-4c63-9b40-4a20388e7563">
<column name="dimension" tableType="STRING" type="STRING" selected="true" primaryKey="false"/>
<column name="amount" tableType="STRING" type="STRING" selected="true" primaryKey="false"/>
<column name="text" tableType="STRING" type="STRING" selected="true" primaryKey="false"/>
<addRowNumber>false</addRowNumber>
<stringDateConverter></stringDateConverter>
<trimStrings>true</trimStrings>
<columnSeparator>,</columnSeparator>
<commentMarker>#</commentMarker>
<dataAsString>dimension, amount, text
a, 10,some text
b, 20, some more text
c, ,text without an amount</dataAsString>
</memoryDataTable>
</inMemoryDS>
<multiLevelDimension dataTableId="d9429713-9be8-4c63-9b40-4a20388e7563" isTimeDimension="false" isDefaultTimeDimension="false" isIndexingByRange="false" id="86d118f0-71ba-4826-a6ac-343eac96fb05" name="Dimension">
<multiLevelHierarchy hasAllLevel="true" allLevelName="All-Level" allMemberName="All" name="Dimension" isDefault="true">
<level name="Dimension - L" nameUnique="false" nameUniqueInParent="false" keyUnique="false" ignoreNameCollision="false">
<nameCol name="dimension"/>
<orderType>BY_NAME</orderType>
<orderKind>ASC</orderKind>
</level>
</multiLevelHierarchy>
</multiLevelDimension>
<cube id="caa9c520-f953-4c77-9e72-76c8668170f7" name="Cube">
<defaultFacts measureGroupName="Facts" partitioningLevelName="" partitioningType="NONE" newGeneration="true" dataTableId="d9429713-9be8-4c63-9b40-4a20388e7563" aggregateDataSourceFacts="false" unresolvedRowsBehavior="ERROR">
<rowFactAggregationType>ADD_ROW</rowFactAggregationType>
<measure name="Amount" aggregationType="SUM">
<dataColumn name="amount"/>
</measure>
<measure name="Text" aggregationType="MIN">
<dataColumn name="text"/>
</measure>
<links dimensionId="86d118f0-71ba-4826-a6ac-343eac96fb05">
<viewLinks type="LAST_LEVEL">
<toColumns name="dimension"/>
</viewLinks>
</links>
</defaultFacts>
</cube>
</schemaDefinition>
</schemaFactory>
- the mdx
drillthrough
select [Measures].members on 0
, [Dimension].[Dimension].[Dimension - L] on 1
from [cube]
return Name([Dimension])
the result
This is not related to having a measure of type STRING.
You're performing a multi-cell result drillthrough (which is an extension of standard MDX in icCube). In that case, the result is "organized" per result cell meaning each [Measures] being in its own category (you can add another Amount measure and you'll see the same behavior).
Instead you should perform a single cell drillthrough:
drillthrough
select [Dimension].[Dimension].[Dimension -L].[a] on 0
from [cube]
And the result should look like:
You can see the [Measures].[Info] being on the same row (as all the other measures).
Hope that helps.
Consider the following code:
<cfset result.enrollments = {} />
<cfset result.enrollments = getCorrectionList(SESSION.id,SESSION.term) />
<cfdump var="#result#" /><Cfabort/>
<cffunction name="getCorrectionList">
<cfargument name="id" required="true" type="string" />
<cfargument name="term" required="true" type="numeric" default="#getCurrentSemester().code#" />
<cfset result = {} />
<cfset result.status = 500 />
<cfset result.message = 'Unknown Error' />
<cfhttp url="THERE IS A REAL URL HERE" />
<cfif cfhttp.statusCode EQ '200 OK'>
<cfset courses = deserializeJson(cfhttp.fileContent,false) />
<cfif courses.recordCount EQ 0>
<cfset result.message = 'You are not currently enrolled in any courses for #ARGUMENTS.term#' />
<cfdump var="#result#" />
<cfreturn result />
</cfif>
<!--- MORE STUFF --->
Now when this runs I get an single struct output with two keys message and status. This is from the dump inside of the getCorrectionList function.
I then get a second struct output with the keys enrollments, message, and status. Inside the enrollments key is another struct with enrollments, message, and status. Inside that enrollments key is another struct with the same keys and so on 50 times with the last struct being empty.
Seems like some recursive actions is going on but where/how?
I have no idea what is going on. As you can see from my code there are no loops. I know the URL resolves correctly and it returns a query and has a recordcount. I can see the data dump at the right spots. But how can the #result# in the function show a single struct but the #result# outside the function show a 50 deep struct repeating itself. It doesn't make any sense.
I dunno why neither Leigh or Scott actually made their comments answers, but they're both right.
You have this reference to result outside your function:
<cfset result.enrollments = getCorrectionList(SESSION.id,SESSION.term) />
And at the end of your function you do this:
<cfreturn result />
Which in effect means you're doing this:
<cfset result.enrollments = result />
Meaning result.enrollments is a reference back to its own parent result.
And <cfdump> is duly displaying the circular reference.
As they both said, you need to localise your variables inside a function, either by varing them:
<cfset var result = {} />
Or explicitly putting them in the local scope:
<cfset local.result = {} />
That will make the function's result be a discrete variable, not simply another reference to the calling code' result variable.
You should always localise your function variables, unless you specifically mean to be referencing a calling-code variable, in which event it makes you code clearer if you explicitly scope it to make it obvious what you mean, eg:
<cfset variables.result = {} />
But this is not what you are meaning to do here. Localise your function's variables.
Given a URL like:
before: http://feeds.wsjonline.com/~r/wsj/health/feed/~3/felKuQPa41U/
which redirects eventually to:
after: http://blogs.wsj.com/health/2009/08/14/insurance-salesman-to-obama-why-are-you-vilifying-insurers/
Using Coldfusion, how can I obtain that final (after) URL? I believe CFHTTP will redirect automatically up to 4 times, but I can't find a way to obtain that final redirected URL.
ideas? thxs
Searching Google may help, sometimes. http://www.bennadel.com/blog/934-Ask-Ben-Handling-Redirects-With-ColdFusion-CFHttp.htm
if you get a redirect with cfhttp you have two options. 1) you can follow (as you say, up to 4 of them in a row). You could also handle them manually by not following them and checking the location variable of the result. THe code would be something like this (note that this is psudo-coldfusion, my syntax might be off:
<cfset lastgoodURL = "http://bar.com" />
<cfset foo = false />
<cfloop while="foo eq false">
<cfhttp url="#lastgoodURL#" redirect="false" name="baz" />
<cfif length(baz.responseHeader.Location) eq 0>
<cfbreak />
</cfif>
<cfset lastgoodURL = baz.responseHeader.Location />
</cfloop>