MDX - calculate one date dimension from another date dimension - date

I have a fact table that has 2 dates Invoice Date and Accounting Current Date. In order to get requested Revenue value I need to use combination of these two dates. For example, if I need YTD Revenue I need to select it like this:
(Note: I am writing SQL query because I am more familiar with it)
SELECT Revenue
FROM
Fact_Revenue
WHERE
Invoice_Date <= '2011-10-22'
and AccountingCurrent >= '2011-01'
and AccountingCurrent <= '2011-10'
Besides Revenue, this fact tables has other information that I also need, but for calculating this other data I don't need Accounting Current Date. So my idea is to use only 1 date (Invoice Date) in main MDX query (so that I can grab as many data with 1 query as I can) and for calculating Revenue I would like to use Calculated Member and in there I would like to associate Accounting Current Date with selected Invoice Date.
For example
SELECT {[Measure].[RevenueYTD],
[Measure].[RevenueMTD],
[Measure].[NumberOfInvoices],
[Measure].[NumberOfPolicies]}
ON COLUMNS,
{[People].Members} ON ROWS
FROM [Cube]
WHERE
[Invoice Date].[Date Hierarchy].[Date].&[2011-10-22]
In this case, [Measure].[RevenueYTD] and [Measure].[RevenueMTD] need to be limited by Accounting Current Date and Invoice Date must be lower than the date from the query. On the other hand, I need [Measure].[NumberOfInvoices] and [Measure].[NumberOfPolicies] for particual Invoice Date (or MTD Date, whatever), but without involvemenet of Accounting Current Date
Calculated member query should do something like this (this is more like algorithm):
ROUND(
SUM(
YTD([Accounting Current Date].[Date Hierarchy].CurrentMember),
[Measures].[Revenue]
),
2)
WHERE [Invoice Current Date].[Date Hierarchy] < [Invoice Current Date].[Date Hierarchy].CurrentMember

Navigating from one dimension to another is not something trivial in MDX. In theory dimensions are independent so standard language is missing functions for doing this. You can use StrToMember MDX function but it's slow and a bit strange.
For your filters, let's start with the first one :
Invoice_Date <= '2011-10-22'
In MDX we'll have to create a set with the members matching the expression. This can be done using the Range set operator :
NULL:[Invoice Date].[Date Hierarchy].[Date].&[2011-10-22]
The other filter is easy to guess :
AccountingCurrent >= '2011-01' and AccountingCurrent <= '2011-10'
MDX version :
[Accounting Date].[Date Hierarchy].[Date].&[2011-01-31]:[Accounting Date].[Date Hierarchy].[Date].&[2011-10-30]
It's also possible using Filter MDX function if your need different type of filters.
Now we need to take the pieces and build the query. One possible solution is using a set slicer and overwritting the values when you don't want the filter to be applied :
WITH
// here we're changing the 'selection' from the where clause
MEMBER [Measure].[NumberOfInvoices II] AS ([Accounting Date].[Date Hierarchy].defaultmember,[Measure].[NumberOfInvoices])
SELECT
.. axis here [Measure].[RevenueYTD] will be applying the filters defined in the where clause
FROM MyCube
WHERE {[Accounting Date].[Date Hierarchy].[Date].&[2011-01-31]:[Accounting Date].[Date Hierarchy].[Date].&[2011-10-30]}

Related

First date with sales greater than 100 in TABLEAU

I have a very Basic flat file with Sales by date and product names. I need to create a field for First sales day where sales are greater than 100 units.
I tried {FIXED [Style Code]: MIN([Prod Cal Activity Date])} but that just gives me the first day in the data the Style code Exists
I also tried IF ([Net Sales Units]>200) THEN {FIXED [Style Code]: MIN([Prod Cal Activity Date])}END but that also gives me the first day in the data the Style code Exists
DATA EXISTS PRIOR TO SALES DATE
You can use the following calculation:
MIN(IF([Net Sales Units]>100) THEN [Prod Cal Activity Date] ELSE #2100-01-01# END)
The IF([Net Sales Units]>100) THEN [Prod Cal Activity Date] ELSE #2100-01-01# END part of the calculation converts the date into a very high value (year 2100 in the example) for all the cases where the sales was more than 100 units. Once this is done, you can simply take a minimum of the calculated date to get the desired result. If you need this by style code, then you can add a fixed function in the beginning.
A few ways to simplify further if you like. They don't change the meaning.
You don't need parenthesis around boolean expressions as you would in C.
You can eliminate the ELSE clause altogether. The if expression will default to null in cases where the condition was false. Aggregation functions like MIN(), MAX(), SUM() etc silently ignore nulls, so you don't have to come up with some default future date.
So MIN(IF [Net Sales Units] > 100 THEN [Prod Cal Activity Date] END is exactly equivalent, just a few less characters to read.
The next possible twist has a bit of analytic value beyond just saving keystrokes.
You don't need to hard code the choice of aggregation function into the calculation. You could instead name your calculated field something like High Sales Activity Date defined as just
if [Net Sales Units] > 100 then [Prod Cal Activity Date] end
This field just holds the date for records with high sales, and is null for records with low sales. But by leaving the aggregation function out of the calculation, you have more flexibility to use it in different ways. For example, you could
Calculate the earliest (i.e. Min) high sales date as requested originally
Calculate the latest high sales date using Max
Filter to only dates with high sales by filtering special non-null values
Calculate the number of high sales dates using COUNTD
Simple little filtering calculations like this can be very useful - so called because of the embedded if statement effectively filters out values that don't match the condition. There are still null values for the other records, but since aggregation functions ignore nulls, you can think of them as effectively filtered out by the calculation.

How can I set a non-continuous date filter?

I need to be able to filter my data to comparable weeks/months across various years. And I need to be able to update those choices on the fly. Is there a way in tableau to set a non-continuous date filter?
because the base data is saved on the tableau server, I'm unable to join additional tables to it, so my initial idea of making a table of year / start date / end date and joining that to my data with a simple t/f filter isn't panning out.
I've considered making series of parameters, but it seems like it might get a bit overwhelming to make so many
Other things I've considered is writing an extended rule like
IF [year] = 2015 THEN [date]BETWEEN(date A, date B)
or the tableau equivalent to BETWEEN
ELSEIF [year] = 2016 THEN [date]BETWEEN(date C, Date D)
ELSE IF //and so forth
Does anyone else have experience with this? Any strategies you might recommend on the parameters vs. a long formula?
Any ideas are greatly appreciated!
Standard tableau filters on dates allow filtering by both continuous date ranges or filtering by parts of dates.
For example, if you wanted to compare January sales cross multiple years, you can drag your date field to the filters shelf and then right-click the date field in the shelf and choose "Discrete" rather than "Continuous" or choose "Month" in the upper set of options about which date component you want (the upper set selects date components, the lower set selects date ranges with in filters and axes).
So, if you put a date field on the filters shelf and select "month" as a discrete you will get a filter that allows you to filter for every January in the dataset.

Double aggregation in Tableau using LOD expressions

I am using Tableau to create a custom google analytics dashboard. I have a custom dimension named author in my google analytics view and I would like to group the date of the first page/view by author and by month having a counter.
I successfully get the date of first page/view using MIN([Date]), but I don't figure out how to use LOD expressions to double aggregate a calculation. I tried the following expression, but tableau shows an error saying that the argument I'm trying to count is already an aggregation and can no longer be aggregated.
{INCLUDE [Author] : COUNT(MIN([Date]))}
What did I miss ?
If your are trying to find the minimum of your [Date] field by your [Author] field, you might want to construct a LOD calculation to find first date, by author like so:
//Creates calculated field for [First Date by author]
{FIXED [Author] : MIN([Date])}
You also have asked about 'double aggregating'. Let's say for example that you wanted find the minimum date, by author, but also by a [Topic]. In this case, you could write:
//Creates calculated field for [First Date by author, by Topic]
{FIXED [Author],[PostTopic] : MIN([Date])}
It is also possible to nest FIXED statements within one another. Let's say for example that you wanted to show maximum [PageViews] for a single month by [Author] and also wanted to limit the time span under consideration to to the first 3 months that an author was publishing. In this case, you could write:
//Creates calculated field for [Most page views in a single month within first 3 months, by Author]
//Example assumes [Date] is a monthly (not daily) data
{ FIXED [Author], [Date] <= DATEADD('month', 3, [First Date by author] ) : MAX({FIXED [Author],[Date]:SUM([PageViews])})}

mdx get measures for a date range between startDate and endDate

I need to know what is the rate of booking of beds (Like in an hotel).
The number of beds (summed month per month) for a range of dates, for the bookings that are in the range (including the partial sum of bookings for the dates that are partially in the range)
I created a "booking" fact table with a StartDate and a EndDate with a measure "countSejoursDate" (count(rows)) and a measure "NbrOfBeds" (sum).
I created 2 "wizard time" dimensions linked as following :
I also created a 3rd "wizard time" dimension called "Date" not linked to any fact.
While trying to get the result, using the MDX below, I'm just able to retrieve the count of rows inside a range of dates... but even with this, the value of the 1srt day of each month is false!
with member nbsejsDate as AGGREGATE(
{NULL:LINKMEMBER([Date].[Calendrier].CURRENTMEMBER,[START_DATE].[Start_Calendrier])}
* {LINKMEMBER([DATE].[Calendrier].CURRENTMEMBER, [END_DATE].[End_Calendrier]):NULL}
, [Measures].[countSejoursDate])
select nbsejsDate
on 0
, [Date].[Calendrier].[Jour].&[2015-03-01]:[Date].[Calendrier].[Jour].&[2015-03-31] on 1
from [Cube]
It's a bit strange as what we've here is an many-to-many relation in the form of a start and end date. Trying to make a correct calculation relying on MDX calculation instead of using a many-to-many relation it's tricky and very,very error prone.
There are different possibilities for solving this :
Use a Range (From - To) link type in the link of time dimension in the Facts.
Use a Javascript view to create an new column that is an array of dates (start/end). The many-to-many relation is created on the fly.
This should make a lot easier any calculation, if I understood the problem correctly.
hope it helps

Aggregate by most recent not-null value

I have a dataset with the following columns [ product_id, country_id, date, number_of_installs, cumulative_installs_last_30_days ]
I have no problem applying the standard measures to find the sum, max or average number_of_installs within those three dimensions (product_id, country_id, date(aggregated by month or week)). However, I have not been able to aggregate by cumulative_installs_last_30_days because as that variable is already a cumulative, I need to return the “most recent value” and Tableau does not have that option built-in the aggregation functions.
How do I create a Calculated Field that enables an addicional column in the aggregated dataset with the most recent not-null value of cumulativeInstalls_last_30_days within the dimensions product_id, country_id and date(aggregated by month or week)?
Here's a dirty solution.
In the comments, you noted that you wanted that 30 days to be dynamic, so to accomplish that, create a parameter, make it an integer, select Range, and allow any integer over zero. I'll call it [Number of Days].
Then create a calculated field:
TOTAL(SUM(IIF(DATEDIFF("day", [date], TODAY()) < [Number of Days], [Number of Installs], NULL)))
I know that's ridonk, so I'll break it down, from the inside out.
DATEDIFF("day", [date], TODAY())
That just calculates the difference in days between today and the date in a given row.
IIF(DATEDIFF("day", [date], TODAY()) < [Number of Days], [Number of Installs], NULL)
That checks if that difference is less than the number of days you selected. If it is, this statement is equal to the number of installs. If it's not, it's null. As a result, if we sum all of these values, we only get the number of installs in the last [Number of Days] days.
With that in mind, we SUM() the rows. TOTAL() just performs that sum over every database row that contributes to the partition.
Note that if your database has dates after TODAY(), you'll need to add another condition to that IIF() statement to make sure those aren't included.
You also mentioned that you want to be able to aggregate the number of installs by month. That's MUCH easier. Just toss MONTH([date]) into the dashboard, then SUM([Number of Installs]), and Tableau will knock it out for you.