I have a task to convert existing MDX measures (from multidimensional model) into DAX (tabular model). During this task I found that in DAX there is no functionality to use "CREATE MEMBER" option as it was in MDX. But I have some members created inside this cube by this function (not in DWH). In this case I'm trying to figure out how to do the same (equivalent) in tabular model (DAX)
There is a part of code which I'm replacing right now:
CREATE MEMBER CURRENTCUBE.[Condition].[Condition].[All].[NEW+USED]
AS [Condition].[Condition Type].[NEW]+[Condition].[Condition Type].[USED]
Image:
There is an example of [Condition] table from DWH:
I have an idea to create a VIEW based on this table with UNION to add a new row "NEW+USED" inside this VIEW and than use SWITCH inside cube (DAX function) for ALL measures
For example:
NVC:=
VAR GALC = [ABC] + [CDE]
RETURN SWITCH(
SELECTEDVALUE('Condition'[ConditionTotal]);
"ConditionTotal"; GALC;
"NEW+USED"; CALCULATE(
GALC;
FILTER(ALL('Condition'[ConditionDescription]); 'Condition'[ConditionDescription] = "New" && 'Condition'[ConditionDescription] = "Used")
)
)
But I'm not sure if it is correct way or not because in this case I should populate all columns from table and some of them using in relationships to Fact tables inside cube.
The only one good solution which I found for now in general looks like this:
Create a new database object (VIEW, for example) in your database with full list of recurred members (so the members will be created on database side instead of SSAS).
Create measure and add additional logic for these members inside your measure.
For example of measure:
My_measure:=
VAR New_var = CALC(SUM('FactTable'[Price]), 'Condition'[ConditionDescription] = "New")
VAR Used_var = CALC(SUM('FactTable'[Price]), 'Condition'[ConditionDescription] = "Used")
VAR New_and_Used_var = CALC(SUM('FactTable'[Price]), ALL('Condition'), 'Condition'[ConditionDescription] in {"New","Used"})
RETURN
SWITCH (SELECTEDVALUE('Condition'[ConditionDescription]),
"New", New_var,
"Used",Used_var,
"New + Used", New_and_Used_var
)
The only one thing which I additionally did is I make reference between my "FactTable" and this new database object (View) as INACTIVE (unchecked "Active" flag in reference window) because it worked not correctly with this flag.
Related
I have a column named Con_TYPE in which there are multiple types of connections such as fiberoptic, satellite, 3g etc.
And I want to sort them only into 2 rows:
fiberoptic
5
others
115
Can anybody help me?
Thanks in advance
You can use Calculated dimension or Mapping load
Lets imagine that the data, in its raw form, looks like this:
dimension: Con_TYPE
measure: Sum(value)
Calculated dimension
You can add expressions inside the dimension. If we have a simple if statement as an expression then the result is:
dimension: =if(Con_TYPE = 'fiberoptic', Con_TYPE, 'other')
measure: Sum(Value)
Mapping load
Mapping load is a script function so we'll have to change the script a bit:
// Define the mapping. In our case we want to map only one value:
// fiberoptic -> fiberoptic
// we just want "fiberoptic" to be shown the same "fiberoptic"
TypeMapping:
Mapping
Load * inline [
Old, New
fiberoptic, fiberoptic
];
RawData:
Load
Con_TYPE,
value,
// --> this is where the mapping is applied
// using the TypeMapping, defined above we are mapping the values
// in Con_TYPE field. The third parameter specifies what value
// should be given if the field value is not found in the
// mapping table. In our case we'll chose "Other"
ApplyMap('TypeMapping', Con_TYPE, 'Other') as Con_TYPE_Mapped
;
Load * inline [
Con_TYPE , value
fiberoptic, 10
satellite , 1
3g , 7
];
// No need to drop "TypeMapping" table since its defined with the
// "Mapping" prefix and Qlik will auto drop it at the end of the script
And we can use the new field Con_TYPE_Mapped in the ui. And the result is:
dimension: Con_TYPE_Mapped
measure: Sum(Value)
Pros/Cons
calculated dimension
+ easy to use
+ only UI change
- leads to performance issues on mid/large datasets
- have to be defined (copy/paste) per table/chart. Which might lead to complications if have to be changed across the whole app (it have to be changed in each object where defined)
mapping load
+ no performance issues (just another field)
+ the mapping table can be defined inline or loaded from an external source (excel, csv, db etc)
+ the new field can be used across the whole app and changing the values in the script will not require table/chart change
- requires reload if the mapping is changed
P.S. In both cases selecting Other in the tables will correctly filter the values and will show data only for 3g and satellite
I created the calculated member as described below in SSMS (not VS, therefore I did not deploy it) by selecting the command and executing it. Accessing this member in MDX works out fine.
But Excel doesn't show me this measure, and I also do not see it in the cube browser. I expect to find it below the measure groups.
Question: What did I miss?
Additional question: How can I place this measure in a measure group?
CREATE MEMBER [Logistics].[Measures].[Printed] AS
SUM
(
{
(
[Pick].[Pick Method].&[4]
, EXCEPT([Pick].[Pick Type].MEMBERS, [Pick].[Pick Type].&[A])
, EXCEPT([Loc].[Build Zone].MEMBERS, {[Loc].[Build Zone].[All], [Loc].[Build Zone].&[G1], [Loc].[Build Zone].&[G2], [Loc].[Build Zone].&[G3]})
, EXCEPT([Loc].[Loc Code].MEMBERS, {[Loc].[Loc Code].[All], [Loc].[Loc Code].[EXPRESS]})
)
}
, [Measures].[Nb of Pick lines]
) ;
Executing this using this MDX-SELECT works out fine and returns a sensible result:
SELECT
NON EMPTY
{
[Location].[Build Zone].MEMBERS, [Location].[Build].[Tested]
} ON COLUMNS
, NON EMPTY {[Calendar].[Calendar Year].[Month].&[2019]&[August].children} ON ROWS
FROM [Logistics] ;
In SSMS you can only create Session-scoped calculated members, as described here
And what you did is that you successfully created this member, and in the same query window if you run this query:
select measures.allmembers on 0
from [Logistics]
you will actually see [Measures].[Printed] member. But as soon as you open a New Query window in SSMS on this cube, and run select measures.allmembers on 0 from [Logistics] again, you will not see your calculated member anymore.
So, the solution is to add this create member script in the Calculation Script in Visual Studio, as you mentioned, and to deploy the cube.
Additionally, to place a measure in measure group, or in some folder under it you can use ASSOCIATED_MEASURE_GROUP and DISPLAY_FOLDER properties. So something like this:
create member [MyCube].Measures.MyMeasure as 999, ASSOCIATED_MEASURE_GROUP = 'My measure group', DISPLAY_FOLDER = 'My display folder'
I am trying to create a dynamic pivoting in postgresql. my query is including 3 dimensions in which one includes a case statement. and one measure. I have tried various solutions found on web but none is working.
I am looking for a script which converts normal query to a pivoted table. pls help me with this.
You have 3 options, basically:
write your query in MDX, which can easily return a pivoted table; requires Mondrian schema first;
use a Kettle datasource and denormalise your data with PDI;
write a denormalisation function in your postFetch method of the table component: it gets the data coming from the query and manipulates it before passing it to the table renderer.
Code snippet to guide you through the process of denormalising in the postFetch of the component:
function(data){
var resultset = data.resultset;
var metadata = data.metadata;
// doStuff - this is the bit you'll have to code according to your needs. The resultset array has 1 element for each row of data; the metadata array holds metadata info such as colName, colType and output index.
var newResultset = someFunction(resultset);
var newMetadata = someOtherFunction(metadata);
// set data to the new values
data.resultset = newResultset;
data.metadata = newMetadata;
// return the modified data object to the component
return data;
}
I've got a few tables, Deployment, Deployment_Report and Workflow. In the event that the deployment is being reviewed they join together so you can see all details in the report. If a revision is going out, the new workflow doesn't exist yet new workflow is going into place so I'd like the values to return null as the revision doesn't exist yet.
Complications aside, this is a sample of the SQL that I'd like to have run:
DECLARE #WorkflowID int
SET #WorkflowID = 399 -- Set to -1 if new
SELECT *
FROM Deployment d
LEFT JOIN Deployment_Report r
ON d.FSJ_Deployment_ID = r.FSJ_Deployment_ID
AND r.Workflow_ID = #WorkflowID
WHERE d.FSJ_Deployment_ID = 339
The above in SQL works great and returns the full record if viewing an active workflow, or the left side of the record with empty fields for revision details which haven't been supplied in the event that a new report is being generated.
Using various samples around S.O. I've produced some Entity to SQL based on a few multiple on statements but I feel like I'm missing something fundamental to make this work:
int Workflow_ID = 399 // or -1 if new, just like the above example
from d in context.Deployments
join r in context.Deployment_Reports.DefaultIfEmpty()
on
new { d.Deployment_ID, Workflow_ID }
equals
new { r.Deployment_ID, r.Workflow_ID }
where d.FSJ_Deployment_ID == fsj_deployment_id
select new
{
...
}
Is the SQL query above possible to create using LINQ to Entities without employing Entity SQL? This is the first time I've needed to create such a join since it's very confusing to look at but in the report it's the only way to do it right since it should only return one record at all times.
The workflow ID is a value passed in to the call to retrieve the data source so in the outgoing query it would be considered a static value (for lack of better terminology on my part)
First of all don't kill yourself on learning the intricacies of EF as there are a LOT of things to learn about it. Unfortunately our deadlines don't like the learning curve!
Here's examples to learn over time:
http://msdn.microsoft.com/en-us/library/bb397895.aspx
In the mean time I've found this very nice workaround using EF for this kind of thing:
var query = "SELECT * Deployment d JOIN Deployment_Report r d.FSJ_Deployment_ID = r.Workflow_ID = #WorkflowID d.FSJ_Deployment_ID = 339"
var parm = new SqlParameter(parameterName="WorkFlowID" value = myvalue);
using (var db = new MyEntities()){
db.Database.SqlQuery<MyReturnType>(query, parm.ToArray());
}
All you have to do is create a model for what you want SQL to return and it will fill in all the values you want. The values you are after are all the fields that are returned by the "Select *"...
There's even a really cool way to get EF to help you. First find the table with the most fields, and get EF to generated the model for you. Then you can write another class that inherits from that class adding in the other fields you want. SQL is able to find all fields added regardless of class hierarchy. It makes your job simple.
Warning, make sure your filed names in the class are exactly the same (case sensitive) as those in the database. The goal is to make a super class model that contains all the fields of all the join activity. SQL just knows how to put them into that resultant class giving you strong typing ability and even more important use-ability with LINQ
You can even use dataannotations in the Super Class Model for displaying other names you prefer to the User, this is a super nice way to keep the table field names but show the user something more user friendly.
I've got an existing advanced search method in a repository that checks a FormCollection for the existence of search criteria, and if present, adds a criterion to the search e.g.
public IList<Residence> GetForAdvancedSearch(FormCollection collection)
{
var criteria = Session.CreateCriteria(typeof(Residence))
.SetResultTransformer(new DistinctRootEntityResultTransformer());
if (collection["MinBedrooms"] != null)
{
criteria
.Add(Restrictions.Ge("Bedrooms", int.Parse(collection["MinBedrooms"])));
}
// ... many criteria omitted for brevity
return criteria.List<Residence>();
}
I've also got a basic distance search to find how far each residence is from the search criteria. The HBM for the query is
<sql-query name="Residence.Nearest">
<return alias="residence" class="Residences.Domain.Residence, Residences"/>
<return-scalar column="Distance" type="float"/>
SELECT R.*, dbo.GetDistance(:point, R.Coordinate) AS Distance
FROM Residence R
WHERE Distance < 10
ORDER BY Distance
</sql-query>
I had to define a function to calculate the distance, as there was no way to get NHibernate to escape the colons in the geography function:
CREATE FUNCTION dbo.GetDistance
(
#firstPoint nvarchar(100),
#secondPoint GEOMETRY
)
RETURNS float
AS
BEGIN
RETURN GEOGRAPHY::STGeomFromText(
#firstPoint, 4326).STDistance(#secondPoint.STAsText()) / 1609.344
END
And the repository calls the named query thus:
return Session
.GetNamedQuery("Residence.Nearest")
.SetString("point", String.Format("POINT({0} {1})", latitude, longitude))
.List();
So my question is; how do I combine the two (or start from scratch), so I can filter the advanced search results to include only residences within 10 miles of the search location?
UPDATE I have tried using NHibernate.Spatial with the following code:
criteria.Add(SpatialExpression.IsWithinDistance(
"Coordinate", new Coordinate(latitude, longitude), 10));
but SpatialExpression.IsWithinDistance returned a System.NotImplementedException.
Have you seen the NHibernate.Spatial project? This may provide an easy solution to your problem.
The alternative is to create your own implementation of ICriterion - this is not too tricky if you derive from AbstractCriterion and you target your particular database platform. This would then allow you to combine your distance function with other criteria.
Create a projection that, in effect, adds a new distance column to the results, which is calculated by called the UDF, and then add a restriction to it:
var query = String.Format(
"dbo.GetDistance('POINT({0} {1}', Coordinate) AS Distance",
latitude, longitude);
criteria
.Add(Restrictions.Le(Projections.SqlProjection(
query,
new [] {"Distance"},
new [] {NHibernateUtil.Double}), 10));
UPDATE
n.b. Although this must have worked when I posted it, it doesn't work any more. NHibernate doesn't like the '.' after dbo, and says
"could not resolve property: dbo of: Residences.Domain.Residence".
If I remove the 'dbo.' I get
"'GetDistance' is not a recognized built-in function name."