# MDX Numeric Functions: The Max() Function

Monday Dec 31st 2007 by William Pearson

Business Intelligence Architect Bill Pearson introduces the numeric Max()function, and leads hands-on practice examples of the basic concepts.

This article is a member of the series, MDX Essentials. The series is designed to provide hands-on application of the fundamentals of the Multidimensional Expressions (MDX) language, with each tutorial progressively adding features designed to meet specific real-world needs.

For more information about the series in general, as well as the software and systems requirements for getting the most out of the lessons included, please see my first article, MDX at First Glance: Introduction to MDX Essentials.

Note: Current updates are assumed for MSSQL Server, MSSQL Server Analysis Services, and the related Books Online and Samples.

### Overview

In this article, we will introduce a “staple” MDX numeric function, the Max() function. Max() is one of several aggregate functions with which we can choose to perform aggregations upon a set of values. (We introduce other aggregate functions within individual articles of the MDX Essentials series.) Max(), like the rest of these functions, aggregates a set of(one or more) measure values associated with a set of dimension members. Simple examples might include the selection of the maximum revenue from a set of products or the maximum monthly personnel headcount over a range of months.

As most of us are already aware, Max() can be leveraged throughout a wide range of activities, from the generation of maximums from simple sets of dimensional members to the composition of multidimensional juxtapositions, for more sophisticated results. As is the case with many MDX functions, Max() can serve as an excellent tool to support sophisticated conditional logic, as well as other calculations, and to deliver exactly the analysis and reporting presentations required by our clients and employers. We will introduce the function, commenting upon its operation and touching upon creative effects that we can employ it to deliver. As a part of our discussion, we will:

• Examine the syntax surrounding the function;
• Undertake illustrative examples of the uses of the function in practice exercises;
• Briefly discuss the results datasets we obtain in the practice examples.

### The Max() Function

#### Introduction

According to the Analysis Services Books Online, the Max() function “returns the maximum value of a numeric expression that is evaluated over a set.” Max() has myriad applications, some obvious and intuitive to even the most inexperienced MDX users, and others that offer sophisticated solutions to more elaborate business requirements, such as the selection of transaction or balance values as of the “most recent date” within our cubes. The function can be leveraged within queries to create datasets, in reporting applications such as MSSQL Server Reporting Services, for the support of presentations of simple maximums, for the support of intelligent default dates within our report parameters, and a host of other creative and useful effects. The Max() function provides an intuitive option anytime we need to present, in a returned dataset, the largest value(s) for a set, based upon a numeric expression and a set we supply. As is the case with most MDX functions, combining Max() with other functions allows us to further extend its power, as we shall see in the practice exercises that follow.

We will examine the syntax for the Max() function after a brief discussion in the next section. We will then explore, from the straightforward context of MDX queries, and within practice examples constructed to support hypothetical business needs, some of the uses it offers the knowledgeable user. This will allow us to activate what we learn in the Discussion and Syntax sections, and allow us to get some hands-on exposure in creating expressions that employ the Max() function.

#### Discussion

To restate our initial explanation of its operation, the Max() function returns the maximum value of a numeric expression we supply, for the members of a set which we also specify. Max() can be used for a great deal more than simple “maximum value” retrieval, as we have intimated. When we couple it with other functions or employ it within MDX scripts, among other applications, we can leverage Max() to support a wide range of analysis and reporting utility.

Let’s look at some syntax illustrations to further clarify the operation of Max().

#### Syntax

Syntactically, we employ the Max() function by specifying a Set Expression (a valid MDX expression that returns the set over which we are attempting to return an associated “maximum” value), followed by a Numeric Expression (a valid numeric expression that is typically an MDX expression of cell coordinates that returns a number) within parentheses to the immediate right of the function. The function takes the Set Expression and Numeric Expression thus appended as its arguments (the two expressions are separated by a comma), and evaluates the Numeric Expression across the set. The maximum value from that evaluation is returned. (If a Numeric Expression is not specified, the set specified by the Set Expression is evaluated in the current context of the members of the set, and the maximum value for the evaluation is returned.) Analysis Services ignores nulls when calculating the maximum value within a set of numbers. Because Max() returns a numeric value, we use it most commonly within the construction of calculated members.

The general syntax is shown in the following string:

`Max(Set_Expression, Numeric_Expression)`

Putting Max() to work is straightforward. When using the function to return the maximum value(s) of the Numeric Expression we have provided as evaluated over the set we have specified, we simply supply the required Set and Numeric Expressions within the parentheses to the right of the Max keyword. As an example, say we create, within a query executed against the sample Adventure Works cube, a calculated member which we might name “Top Orders,” (that is, say, for the “highest value for the Internet Order Count measure”) containing the following pseudo code:

```MAX([Date].[Calendar Year].[CY 2001]:[Date].[Calendar Year].[CY 2004],
[Measures].[Internet Order Count] )```

Moreover, say that we select the set of the same Date range dimensional members and the calculated member within our column axis, and the Product Category members within our row axis, we might expect to retrieve results similar to those depicted in Illustration 1.

Illustration 1: Example Returned Data: Max() Function Employed in Calculated Member

It is easy to see, within the dataset returned above, that the maximum value, based upon the members of the dimensional date range we have provided, is returned, as the calculated member value presents the highest value within each of the rows retrieved. Because of the ease with which we can employ Max(), and because of the flexibility with which we can exploit it to meet various business needs, the function becomes a popular member of our analysis and reporting toolsets. We will practice some uses of the Max() function in the section that follows.

#### Practice

Preparation: Access SQL Server Management Studio

To reinforce our understanding of the basics we have covered, we will use the Max() function within queries that illustrate its operation. The intent is to demonstrate the use of Max() in a straightforward, memorable manner that efficiently illustrates its operation.

We will turn to the SQL Server Management Studio as a platform from which to construct and execute the MDX we examine, and to view the results datasets we obtain. If you do not know how to access the SQL Server Management Studio in preparation for using it to query an Analysis Services cube (we will be using the sample Adventure Works cube in the Adventure Works DW Analysis Services database), please perform the steps of the following procedure, located in the References section of my articles index:

Prepare MSSQL Server Management Studio to Query Analysis Services

This procedure will take us through opening a new Query pane, upon which we will create our first query within the section that follows.

Procedure: Satisfy Business Requirements with MDX

Let’s construct a simple query to provide a conceptual “starting point” for illustrating the use of the Max() function. The idea is to generate a basic dataset that shows the concepts behind using the Max() function and to introduce some of the ways we can employ it effectively. Once we have accomplished our immediate goal in our first practice example, we will further evolve these concepts in meeting another business requirement in the procedure that follows it.

Procedure: Use the Max() Function to Generate a Simple “Maximum Value” in a Results Dataset

Let’s say that information consumers from the Adventure Works Logistics department, whose data is housed within the Adventure Works cube, come to us with a straightforward request: The consumers wish to see the top Internet Sales Amounts, based upon the organization’s Sales Territory Countries, for each of the Product Category groups offered by Adventure Works to its customers. They prefer to present the totals (leaving out nulls) for the member Product Categories sold by each Country, along with the highest values among those Country totals (in a column called “Top Sales”), on each row of the returned data set.

The basic Max() function involved will be housed within a calculated member we will call Top Sales. We will then employ the calculated member within a core query that presents the information in the manner requested. Our query will be constructed as described in the following procedure.

1.  Type (or cut and paste) the following query into the Query pane:

```
-- MDX062-1: Basic Use of MAX() Function

WITH

MEMBER

[Sales Territory].[Sales Territory].[Top Sales]

AS

'MAX(  { [Sales Territory].[Sales Territory].[Country].MEMBERS},

[Measures].[Internet Sales Amount])'

SELECT

{ [Sales Territory].[Sales Territory].[Country].MEMBERS,

[Sales Territory].[Top Sales]  } ON AXIS(0),

NON EMPTY   {[Product].[Product Categories].[Category].MEMBERS} ON AXIS(1)

FROM

WHERE

([Measures].[Internet Sales Amount])
```

Our query is simply expressing that we wish to retrieve the “total value of Internet Sales,” for each of our general Product Categories, by Sales Territory Country, alongside a repetition of the highest value within each row, within a column we label “Top Sales.” We are ignoring time – the consumers are aware that the cube contains data from several years, and they want the information to be based upon the cube as a whole for their present purposes. Moreover, they realize that, once they use the scenario we have assembled to verify effective operation of the Max() function (inside the “Top Sales” calculation we have built), they may jettison the individual Sales Territory Country columns, at least in reports they create later, based upon our example. (Parameterization, in an application like Reporting Services, becomes an easy objective, of course.)

The Query pane appears, with our input, as shown in Illustration 2.

Illustration 2: Our Initial Query in the Query Pane ...

The above query demonstrates the basic use of Max(), and accomplishes the objective of illustrating, in the simplest manner, how it works. The idea is to generate a dataset to activate the concepts in the minds of our client colleagues.

2.  Execute the query by clicking the Execute button in the toolbar, as depicted in Illustration 3.

Illustration 3: Click Execute to Run the Query...

The Results pane is populated by Analysis Services, and the dataset, shown in Illustration 4, appears.

Illustration 4: Results Dataset – Simple “Maximum Value” Scenario

In the returned dataset, we see the members of the Country level of the Sales Territory dimension (Sales Territory hierarchy) appear upon the column axis, and the members of the Category level of the Product dimension (Product Categories hierarchy) appear upon the rows axis. We have retrieved, for each intersection, the non-null Internet Sales Amount values, together with the value of the calculated member Top Sales that corresponds with each Product Category row. This simple dataset provides a great “beginner” illustration of the output of Max(), as we can see that the function generates the correct maximum value that appears in the respective Country column to its left.

NOTE: For more detail surrounding the .Members function, see my article MDX Members: Introducing Members and Member, a member of my MDX Essentials series at Database Journal.

3.  Select File -> Save MDXQuery1.mdx As ..., name the file MDX062-001, and place it in a meaningful location.

Our client colleagues express satisfaction with the contextual backdrop we have established for introducing the Max() function. We will employ the function again in our next steps to expand upon its use in the first example.

Procedure: Use the Max() Function to Select Peak Sales and the Month in Which They Occur

Having demonstrated the basic operation of Max(), we are ready to address another requirement that the client representatives describe. To detail the requirement, our colleagues have asked us to address a specific, immediate need, although they hope to be able to extrapolate the concepts we introduce to other, similar needs that commonly arise within the organization. Our colleagues have asked that we construct a query that presents a slightly more sophisticated dataset, and uses the Max() function in a way that differs from our previous example, where we simply retrieved a maximum measure value for a specified set.

Max(), of course, can be used in numerous ways that lie beyond the obvious selection of the”largest number” or “biggest amount” for a set we use as a basis in the function. We often, for instance, use the function to select the “last” (or “greatest”) month (or other Date period), as an example where we have conducted activity or maintain a balance. The business requirement that the client representatives communicate next contains an element of such a need, as we shall see.

Our colleagues tell us that they wish to support the capability of Logistics users to report upon and analyze peak sales of the Resellers, with which Adventure Works maintains relationships, to market and distribute its products. To begin, they wish to be able to generate a dataset that presents the highest monthly sales values for Resellers who have completed sales within the last Calendar Year, 2003. Moreover, they wish to present, alongside this “peak sales” value, the period within which the sales were achieved (which they wish to label “Peak Period” within the returned dataset), for ease of use for immediate needs, as well as to support some charts they wish to create in Reporting Services at a later time. The client representatives make us aware that they wish to screen out Resellers who have no activity at all over the given Calendar Year, simply to streamline reports and make them more compact.

We explain that the Max() function promises to be useful in generating the desired presentation. We confirm our understanding of the foregoing needs, as well as our conclusion that we have happened upon a great opportunity to both assist the client in meeting its immediate needs as well as to provide examples that leverage the MDX Max() function. We set out to craft a query that relies upon Max(), in conjunction with other the MDX functions, to meet the business need.

The basic Max() function involved will be housed within a couple of calculated members, which we will call Peak Sales and Peak Period, respectively. We will then employ the calculated members within a core query that arranges the information it retrieves in the manner requested. Our query will be constructed as described in the following procedure.

1.  Select File --> New from the main menu.

2.  Select Query with Current Connection from the cascading menu that appears next, as depicted in Illustration 5.

Illustration 5: Create a New Query with the Current Connection ...

A new tab, with a connection to the Adventure Works cube (we can see it listed in the selector of the Metadata pane, once again) appears in the Query pane.

3.  Type (or cut and paste) the following query into the Query pane:

```
-- MDX062-2 More Sophisticated Example of MAX() Function in Use

WITH

MEMBER

[Measures].[Peak Sales]

AS

'MAX(DESCENDANTS( [Date].[Calendar].[Calendar Year].[CY 2003],

[Date].[Calendar].[Month]),

[Measures].[Reseller Sales Amount]
)'

MEMBER

[Measures].[Peak Period]

AS

'IIF(

MAX([Date].[Calendar].[Month].MEMBERS, [Measures].[Reseller Sales Amount]) > 0,

TOPCOUNT(

DESCENDANTS( [Date].[Calendar].[Calendar Year].[CY 2003],

[Date].[Calendar].[Month]),

1,

[Measures].[Reseller Sales Amount]).ITEM(0).NAME, null

)'

SELECT

{[Measures].[Peak Sales], [Measures].[Peak Period]} ON AXIS(0),

NON EMPTY

{[Reseller].[Reseller Type].[Reseller].MEMBERS}

ON AXIS(1)

FROM

```

The Query pane appears, with our input, as shown in Illustration 6.

Illustration 6: Our Second Query in the Query Pane ...

We note that we have taken steps, within the conditional element of the Peak Period calculated measure, to present a null in place of the default that would display (the first period, January, of 2003) were we to simply to allow default behavior. This allows us to screen null rows out via Non Empty. For those Resellers experiencing at least one sale within Calendar Year 2003, we derive a Peak Sales value using the Max() function, specifying the members of the Month level of the Date dimension (Calendar hierarchy) as the Set Expression across which the Numeric Expression representing the Reseller Sales Amount measure is evaluated.

We use the TopCount() function within the creation of the Peak Period calculated measure, effectively stating that we wish to retrieve the first, or “topmost” values that occur within the Months (we use the Descendants() function to specify that the query focus upon the relevant Month level members of Calendar Year 2003)

4.  Execute the query by clicking the Execute button in the toolbar.

The Results pane is, once again, populated by Analysis Services. This time, the dataset partially depicted in Illustration 7 appears.

Illustration 7: Results Dataset – Max() Leveraged to Deliver More Sophistication (Partial View)

In the returned dataset, we see the list of Resellers who have been evaluated as having sales activity in at least one period of Calendar Year 2003. Juxtaposed with these names, we observe the two calculated members we have constructed, Peak Sales (containing the primary use of the Max() function) and Peak Period (within which we make referential use to the Max() function as it exists in the first calculated member – primarily to support conditional logic that nulls the derived Peak Period date, and which allows it to be filtered out via Non Empty). The second calculated member also leverages the combination of the .Item() and .Name functions to generate the month and year format that represents the Peak Period output in the dataset.

NOTE: For more detail surrounding the TopCount() function, see Basic Set Functions: The TopCount() Function, Part I and Basic Set Functions: The TopCount() Function, Part II . For information on IIF(), see String / Numeric Functions: Introducing the IIF() Function and String / Numeric Functions: More on the IIF() Function. For exposure to the Descendants() function, see various articles throughout the MDX Essentials series.

For information on the .Item() function, see Basic Member Functions: The .Item() Function. For an introduction to the .Name function, see String Functions: The .Name Function. For examples of the use of Non Empty, see various articles throughout this series. Finally, for an introduction to the .Members function, see my article MDX Members: Introducing Members and Member.

5.  Select File -> Save MDXQuery2.mdx As ..., name the file MDX062-002.mdx, and place it in the same location used to store the earlier query.

To corroborate the operation of the solution we have proposed above, let’s examine the sales figures concerned for a given reseller over all periods in the year under examination. From this quick test dataset, we can see that our calculated members (each leveraging the Max() function) have served us as expected, both in selection of the appropriate value for Peak Sales and the appropriate month for Peak Period.

6.  Select File --> New from the main menu, as we did earlier.

7.  Select Query with Current Connection from the cascading menu that appears next, once again.

A new tab, with a connection to the Adventure Works cube appears in the Query pane, as before.

8.  Type (or cut and paste) the following query into the Query pane:

```-- MDX062-3 "Check Query" to Ascertain Solution Effectiveness

SELECT

CROSSJOIN(

{DESCENDANTS( [Date].[Calendar].[Calendar Year].[CY 2003],

[Date].[Calendar].[Month])},

{[Measures].[Reseller Sales Amount]}

)

ON AXIS(0),

{[Reseller].[Reseller Type].[Reseller].[Best o' Bikes]:

[Reseller].[Reseller Type].[Reseller].[Bicycle Exporters]}

ON AXIS(1)

FROM

```

The Query pane appears, with our input, as shown in Illustration 8.

Illustration 8: Verifying Our Results with a “Test Data” Query

We have simply constructed a query to generate the Calendar Year 2003 monthly values for Reseller Sales Amount for a couple of the organization’s resellers, Best o’ Bikes and Bicycle Exporters. We will be able, thereby, to easily identify the “highest value” of each that represents Peak Sales value among the months. We can then also see, at a moment’s glance, the month in which these sales took place (and thus confirm the Peak Period involved). We can then compare the results of our sample to the values delivered by our two calculated members, Peak Sales and Peak Period, from the last dataset retrieved by the last query, to determine accuracy – and thereby verify the effectiveness of our approach.

NOTE: For more detail surrounding the CrossJoin() function, see Basic Set Functions: The CrossJoin() Function. For a discussion of the Range (“:”) operator, see my article MDX Operators: The Basics. Both articles are members of my MDX Essentials series at Database Journal.

9.  Execute the query by clicking the Execute button in the toolbar.

The Results pane is, as before, populated by Analysis Services. The dataset depicted in Illustration 9 appears.

Illustration 9: Results Dataset – Confirmation Query

In the returned dataset, we see the values we have discussed appearing for each month in Calendar Year 2003 for each sample Reseller. We also note that the values presented by the Peak Sales calculated member in our solution for each of the two Resellers agrees to what we can easily see is the largest value within the confirmation dataset, and that the month within which the “highest month’s sales” took place in the confirmation dataset agrees to the Peak Period month returned in our solution. The comparisons appear as shown in composite Illustration 10.

Illustration 10: Results Datasets Composite – Indicating That Our Solution is Effective

The Logistics department representatives express satisfaction with the results, and confirm their understanding of the operation of the Max() function within the context we have presented, among other uses we have discussed in earlier sections. We suggest to our client colleagues that, among numerous possibilities, the Resellers (single, multiple, or ranged, etc.), as well as various components of the Date dimension (Calendar or Fiscal Hierarchy, Year, Semesters, Quarters, and so forth), and even the “number of highest values” (say, for example, “top two” or “top five”, etc.), and other components of our query might be parameterized, within their Reporting Services environment. Moreover, we emphasize, we might add other capabilities within the ultimate reporting dataset query.

Suffice it to say that, assuming an “above ordinary” knowledge of the various layers of the Microsoft integrated BI solution, one can obtain many powerful capabilities and features, and knowing “where to put the intelligence” within the sometimes multiple choices can mean highly tuned performance and effective solutions for consumers throughout our organizations. For more of my observations on this subject see Multi-Layered Business Solutions ... Require Multi-Layered Architects.

10.  Experiment with the “test query,” as desired, to examine and compare the values for other Resellers to those delivered by the previous query via the Max() function.

11.  Select File -> Save MDXQuery3.mdx As ..., name the file MDX062-003.mdx, and place it in the same location used to store the earlier query, if and when desired.

12.  Select File -> Exit to leave the SQL Server Management Studio, when ready.

### Summary ...

In this article, we introduced and explored the MDX Max() function, one of several aggregate functions available within the MDX toolset, whose general purpose is to deliver a maximum value of a numeric expression that is evaluated over a set. We emphasized that Max() can be leveraged in a wide range of activities, from the “generation of maximums” from simple sets of dimensional members to multidimensional juxtapositions we can compose to deliver even more sophisticated results. Moreover, we learned that Max() can serve as an excellent tool to support sophisticated conditional logic, as well as other calculations, to deliver exactly the analysis and reporting presentations required by our clients and employers.

We examined the syntax involved with Max(), and then undertook a couple of illustrative practice examples of uses for the function, generating queries that capitalized upon its capabilities. Throughout our practice session, we briefly discussed the results datasets we obtained from each of the queries we constructed, as well as extending our discussion to other possible options and uses for the concepts we exposed.

Discuss this article in the MSSQL Server 2000 Analysis Services and MDX Topics Forum.