DAX - Create Dynamic Index Column - sql

I am trying to create a calculated column in SSAS tabular model with DAX. I want a dynamic index column on a table. Meaning that the index starts at 0 when the table is filtered. Imagine you have a table like:
item index
apple 0
banana 1
celery 2
broccoli 3
If I filter the table to just vegetables normally the index would still be:
item index
celery 2
broccoli 3
But I want it to be
item index
celery 0
broccoli 1
So far I create the index with: (I am indexing a date dimension table)
=CALCULATE(COUNT([Date])-1, ALL('DimDate'), FILTER(DimDate, [Date]<=EARLIER([Date])))
I have tried using ALLEXCEPT() and I have tried making an Offset column by getting the first value of the Index with FIRSTNONBLANK, but have not yet been successful.
Any ideas or help?

You cannot have a dynamic calculated column because calculated columns are evaluated and stored at design time. So you would have to use a measure to do the dynamic indexing. Here is one approach below using ALLSELECTED with additional parsing to display blank as zero.
=
IF (
ISBLANK (
CALCULATE (
COUNTROWS ( DimDate ),
FILTER ( ALLSELECTED ( DimDate ), DimDate[Date] < MAX ( DimDate[Date] ) )
)
),
0,
CALCULATE (
COUNTROWS ( DimDate ),
FILTER ( ALLSELECTED ( DimDate ), DimDate[Date] < MAX ( DimDate[Date] ) )
)
)

Related

Create a SQL report associating thousands of records

I'm a newbie in Oracle SQL and i'm trying to do the following:
I have a list of products with sales only on a certain weeks of the year.
I'm just triying to create a whole report of sales for the year and fill those weeks with no sales with zeroes, but preserving the product ID as identifier.
Trying to create that report, i'm starting trying to create a table with all the weeks of the year and de product ID, the problem is that there are thousands of products. I'm trying to get something like:
WEEK_YR PRODUCT_ID
------ ----------
2018011 Product 1
2018012 Product 1
...
2018053 Product 1
...
2018124 Product 1
...
2018011 Product 2
2018012 Product 2
...
2018053 Product 2
...
2018124 Product 2
And then just join this table with the sales table per week, putting 0 in those null weeks from that table.
I request your help at creating the first table, i just count with a table with the products ID and other with the list of weeks from last year
Thanks in advance!
I request your help at creating the first table...
You don't need to create a persistent table for that. Since Oracle 9.2 you can use CTEs (Common Table Expressions). In the example below the cte w is the one you are looking for:
with x as ( --- cte that generates numbers from 1 to 52
select 1 as n from dual
union all
select n + 1 from x where n < 52
),
w as ( --- cte that generates the week_yr
select 2018000 + n as week_yr from x
)
select -- main query now.
w.week_yr,
p...
from w
left join products p on p.week_yr = w.week_yr
where ...
group by ...
In the query above, the cte x generates numbers from 1 to 52. Then the cte w generates the week_yr values you need.
Finally, the main query can do what you want.

How to summarize on distinct values that group other values

I have this table:
Family_ID Person_ID Weight Spent_amount category
A 1 10 500 flight
A 2 10 500 flight
A 1 10 200 Hotel
A 2 10 200 Hotel
B 3 20 250 flight
B 3 20 300 Hotel
as we see here every family has a member and the costs per category calculated for the family not the person. And as we see not every family is equal to other but it has weight so every spent should be multiplied by family weight.
Now my goal is to write a measure for spent for every family
I wrote an equation but I see its very complex and I guess it give a wrong value. The Dax Equation I wrote is
cash_wieght:=SUMX (
SUMMARIZE (
s2g516_full,
s2g516_full[DIM_HOUSEHOLD_REF_ID],
"cash", CALCULATE ( MAXX ( SUMMARIZE (
s2g516_full,
s2g516_full[Persons],
s2g516_full[DIM_HOUSEHOLD_REF_ID] ,"cash1", CALCULATE ( Sum(s2g516_full[SPENT_PER_REASON])*MAX ( s2g516_full[Weight] ) )
/distinctcount(s2g516_full[person]) ),[Cash1] ) )
),
[cash]
)
When I took a sample by filtering with family_ID, it gave me the correct numbers for samples tests but maybe not of them. So how can I tell if I got the right results or not?
The result I want is the weight * spent_amount to be summed and filtered with family_ID as if person_ID is not in the table like: "if I filtered on Family_ID"
A 700*10
B 550 *20
First, DAX Formatter is your friend.
cash_wieght :=
SUMX (
SUMMARIZE (
s2g516_full,
s2g516_full[DIM_HOUSEHOLD_REF_ID],
"cash", CALCULATE (
MAXX (
SUMMARIZE (
s2g516_full,
s2g516_full[Persons],
s2g516_full[DIM_HOUSEHOLD_REF_ID],
"cash1", CALCULATE (
SUM ( s2g516_full[SPENT_PER_REASON] ) * MAX ( s2g516_full[Weight] )
)
/ DISTINCTCOUNT ( s2g516_full[person] )
),
[Cash1]
)
)
),
[cash]
)
After that, I think you've got the right intuition with SUMMARIZE(), but it can be much simpler than what you're doing. SUMMARIZE() groups by the fields in a table that you pass to it. Since members with the same [Family_ID] have the same values for [Spent_Amount] in a given [Category], all we need to do is group by everything from the table except [Person_ID].
cash_wieght:=
SUMX (
SUMMARIZE(
FactSpend
,FactSpend[Family_ID]
,FactSpend[Weight]
,FactSpend[Category]
,FactSpend[Spent_Amount]
)
,FactSpend[Weight] * FactSpend[Spent_Amount]
)
Here we use SUMMARIZE() to group by [Family_ID], [Weight], [Category], and [Spent_Amount]. Since addition and multiplication are commutative, it doesn't matter if we add first or multiply first.
So we use SUMX() to iterate over each row returned by our SUMMARIZE() and accumulate into a sum the values of [Weight] * [Spent_Amount] for each row in that grouped table.
Here's an image of my sample data and pivot table based on this measure performing appropriately. It also works when adding any of the attributes from the table to filter context. If one [Person_ID] is in context, the measure will return the value for the family, and at the family level will return the value for the family. We can alter the measure to only give a single [Person_ID]'s portion of the [Family_ID]'s whole if necessary.

Powerpivot - Only Show Minimum Value

Newbie to DAX/PowerPivot and struggling with a specific problem.
I have a table
Location Category Distance
1 A 1.244
2 A 2.111
3 B 5.113
4 C 0.124
etc
I need to identify the Minimum distance out of the selection and only output for that record. So I'd have
Location Category Distance MinDist
1 A 1.244
2 A 0.111 0.111
3 B 5.113
4 C 3.124
etc
I've tried various measures but always end up with simply a repeat of the Distance column....whatever filters I try to apply.
Please help.
If your table was called 'table1' then this would give you the overall minimum:
=CALCULATE(MIN(Table1[Distance]), ALL(Table1))
Depending on your requirements, you may have to specify columns in the ALL() to reduce how much of the filter is opened up (suggest you research ALL() as it is a very important DAX function).
To return zero (blanks is tricky) for the non matchers you could package it in:
=
IF (SUM ( Table1[Distance] ) = CALCULATE ( MIN ( Table1[Distance] ), ALL ( Table1 ) ),
CALCULATE ( MIN ( Table1[Distance] ), ALL ( Table1 ) ),
0
)

Mdx - Flag -Actual

I have two dimensions DimFlag and DimPNL and a fact table FactAmount. I am looking to:
When pnl is stat(Is Stat=1) : sum (Actual x FlagId)
For pnl I multiply the amounts by field FlagId basically if it will be so 0 0 X = 0 ...
DimFlag
FlagId FlagLabel
-----------------
1 NotClosed
0 IsClosed
DimPNL
PNLId PNLName Is Stat
1 a 1
2 test 1
3 test2 0
FactAmount
id PNLId FlagId Actual
1 1 1 100
2 2 1 10
3 3 0 120
I tried the following MDX but it didn't work, any idea please ?
Scope (
[Dim PNL].[PNL].members,[Measures].members
);
this = iif([Dim PNL].[PNL].CurrentMember.Properties("Is Stat") =1
,
aggregate([Dim PNL].[PNL].currentmember,[Measures].currentmember)* iif([Dim Flag].[Flag Label].[Flag Label].currentmember = 0, 0, 1),
aggregate([Dim PNL].[PNL].currentmember,[Measures].currentmember)
);
While this type of calculation can be done in MDX, the MDX can get complex and performs bad. I would suggest to explicitly do the calculation e. g. in the DSV or a view on the fact table that you then use instead of the fact table directly in the DSV. The result of the calculation would then be another column on which you can base a standard measure.
To do it in the DSV, assuming you use a relational table as the base for the fact table, add a named calculation to it, define the column name however you like, and use the expression Actual * FlagID. For the other calculation, you may need a subselect, i. e. the expression would be Actual * case when pnlId in(1,2) then 1 else 0 end. You can use any SQL that works as a column expression in the select list as the expression in for a named calculation.
Implementing the same in a view on FactAmount, you could implement the second expression better, as then you could join table DimPNL in the view definition and thus use column IsStat in the calculation. Then you would replace table FactAmout by the view, which has the two additional measure columns.
In either case, just define two measures on the two new columns in the cube, and you are done.
As a rule, calculations that are done on record level in the fact table before any aggregation should be done at data loading time, i. e. as described above.

Reference current row in a computed column with SELECT

I have a table that represents the parameters of a contract - including their change over time through addendums. The first addendum is a "special" addendum representing the parameters when the contract was first signed.
Here's how the table should look like:
ID ProjectID BeginDate DeadlineMonths DeadlineDate
1 20 20-12-2006 24 <computed= 20-12-2006 + 24 months>
2 23 12-03-2007 12 <computed= 12-03-2007 + 12 months>
3 20 06-09-2007 36 <computed= **20-12-2006** + 36 months>
ProjectID is a FK to the Projects table whose primary key is also called ProjectID.
I want DeadlineDate to be a calculated field, calculated like so:
DeadlineDate COMPUTE BY ((
select first 1 AddMonth(contract.BeginDate, DeadlineMonths)
from addendums contract
where contract.projectid = projectid
order by contract.BeginDate ))
The problem is that in contract.projectid = projectid the second ProjectID has to reference the current row being computed, not the current row in the select statement (which is the same as contract.projectid).
I'm using Firebird. I need the column in the table and NOT in a SELECT statement because of ORM issues in the application using the database.
Just prefix the field with table name of the current table:
DeadlineDate COMPUTED BY ((
select first 1 AddMonth(contract.BeginDate, DeadlineMonths)
from addendums contract
where contract.projectid = projects.projectid
order by contract.BeginDate ))
Can you create a view over your query and use it in the ORM?
CREATE VIEW v_addendums
AS
SELECT ID, ProjectID, BeginDate, DeadlineMonths,
(
SELECT first 1 AddMonth(contract.BeginDate, DeadlineMonths)
FROM addendums contract
WHERE contract.projectid = a.projectid
ORDER BY
contract.BeginDate
)
FROM addendums a