由于项目业务需求,需要求出指定日期前六个月每个月最后一个不为零的数字作为最终结果。通过google查找到一篇不错的求从今天开始拿最近一天的不为零的值作为每一天的结果,
网页内容如下(引自http://cwebbbi.wordpress.com/2011/03/24/last-ever-non-empty-a-new-fast-mdx-approach/)
The last non empty semi-additive measure aggregation functionality in SSAS enterprise edition is very useful, but it doesn’t support one common business requirement: while it will give you the last non empty value within any given time period, it doesn’t handle the variation where you want to get the last non empty value of a measure from all preceding time periods (this is what I’m calling the ‘last ever non empty’ value). There are a number of business scenarios where you’d want to do this, for example finding the value of the last purchase a customer made, the last price you sold a product at, and the stock level of a product in a shop the last time a sales rep visited. Traditional MDX solutions to this problem have suffered from poor performance but in this blog post I’ll describe a new approach that performs much better; I think it will be very useful to a lot of people, and I’m quite proud of it!
Let’s take the following MDX query on Adventure Works as an example of the problem:
SELECT HEAD([Customer].[Customer].[Customer].MEMBERS, 10) * {[Measures].[Internet Sales Amount]} ON 0, NON EMPTY [Date].[Date].[Date].MEMBERS ON 1 FROM [Adventure Works]
Here’s part of the results:
From this we can see that individual customers only bought from us once or twice. Now, for any date, let’s create a calculation that will find what the value of the last purchase by any given customer was, regardless of however long ago it was. Up until last week I’d have tackled this problem using a combination of the NonEmpty and Tail functions – for each customer and date, get the set of all preceding dates, find the dates which had values and find the value of the last date. Here’s the code:
WITH MEMBER MEASURES.[Last Sale Original] AS TAIL( NONEMPTY({NULL:[Date].[Date].CURRENTMEMBER} * [Measures].[Internet Sales Amount]) ).ITEM(0) SELECT HEAD([Customer].[Customer].[Customer].MEMBERS, 10) * {[Measures].[Internet Sales Amount],MEASURES.[Last Sale Original]} ON 0, [Date].[Date].[Date].MEMBERS ON 1 FROM [Adventure Works]
And here’s the part of the results dealing with the first customer, Aaron A. Allen:
On my laptop the query takes 14 seconds to run, and that’s with only 10 customers on columns (it executes in cell-by-cell mode, I think); in many real world scenarios this kind of performance isn’t acceptable and that was certainly the case with the customer I was working with last week. So I came up with the following new MDX that does the same thing much faster:
WITH MEMBER MEASURES.DAYSTODATE AS COUNT(NULL:[Date].[Date].CURRENTMEMBER)-1 MEMBER MEASURES.HADSALE AS IIF([Measures].[Internet Sales Amount]=0, NULL, MEASURES.DAYSTODATE) MEMBER MEASURES.MAXDATE AS MAX(NULL:[Date].[Date].CURRENTMEMBER, MEASURES.HADSALE) MEMBER MEASURES.LASTSALE AS IIF(ISEMPTY(MEASURES.MAXDATE), NULL, ([Measures].[Internet Sales Amount], [Date].[Date].[Date].MEMBERS.ITEM(MEASURES.MAXDATE))) SELECT HEAD([Customer].[Customer].[Customer].MEMBERS, 10) * {[Measures].[Internet Sales Amount] ,MEASURES.[LASTSALE]} ON 0, [Date].[Date].[Date].MEMBERS ON 1 FROM [Adventure Works]
On my laptop this query now executes in 3 seconds. Here’s what it’s doing:
If we want to take this approach and apply it to a server-based calculation, and make it work at all levels on the Date dimension, we need a slight variation. Again using the Adventure Works cube to illustrate, here’s what you need to do…
First of all, you need to create a new column in your fact table that contains only null values and use this as the basis of a new real (ie not calculated) measure, which should be called MaxDate. This should have the aggregation function Max.
You then need to add the following code to the MDX Script of the cube:
CREATE MEMBER CURRENTCUBE.MEASURES.DAYSTODATE AS COUNT(NULL:[Date].[Date].CURRENTMEMBER)-1 , VISIBLE=FALSE; CREATE MEMBER CURRENTCUBE.MEASURES.HADSALE AS IIF([Measures].[Internet Sales Amount]=0, NULL, MEASURES.DAYSTODATE) , VISIBLE=FALSE; SCOPE(MEASURES.MAXDATE, [Date].[Date].[Date].MEMBERS); THIS = MAX(NULL:[Date].[Date].CURRENTMEMBER, MEASURES.HADSALE); END SCOPE; CREATE MEMBER CURRENTCUBE.MEASURES.LASTSALE AS IIF(ISEMPTY(MEASURES.MAXDATE), NULL, ([Measures].[Internet Sales Amount], [Date].[Date].[Date].MEMBERS.ITEM(MEASURES.MAXDATE)));
This does basically the same as the previous example only now MaxDate is a real measure instead of a calculated measure, and we’re using a scoped assignment to overwrite its value at the Date level. Above the Date level the default aggregation method of the MaxDate measure kicks in and we see the Max value of MaxDate for all dates in the current time period – which means at the month, quarter and year level we once again get the index of the last non empty date. Here’s what the result looks like in the cube browser:
只要修改MaxDate,让他从每个月开始计算,最终mdx如下