<html xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns:m="http://schemas.microsoft.com/office/2004/12/omml" xmlns="http://www.w3.org/TR/REC-html40">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=us-ascii">
<meta name="Generator" content="Microsoft Word 15 (filtered medium)">
<style><!--
/* Font Definitions */
@font-face
        {font-family:"Cambria Math";
        panose-1:2 4 5 3 5 4 6 3 2 4;}
@font-face
        {font-family:Calibri;
        panose-1:2 15 5 2 2 2 4 3 2 4;}
@font-face
        {font-family:Consolas;
        panose-1:2 11 6 9 2 2 4 3 2 4;}
/* Style Definitions */
p.MsoNormal, li.MsoNormal, div.MsoNormal
        {margin:0in;
        margin-bottom:.0001pt;
        font-size:11.0pt;
        font-family:"Calibri",sans-serif;}
a:link, span.MsoHyperlink
        {mso-style-priority:99;
        color:#0563C1;
        text-decoration:underline;}
a:visited, span.MsoHyperlinkFollowed
        {mso-style-priority:99;
        color:#954F72;
        text-decoration:underline;}
p.MsoListParagraph, li.MsoListParagraph, div.MsoListParagraph
        {mso-style-priority:34;
        margin-top:0in;
        margin-right:0in;
        margin-bottom:0in;
        margin-left:.5in;
        margin-bottom:.0001pt;
        font-size:11.0pt;
        font-family:"Calibri",sans-serif;}
span.EmailStyle18
        {mso-style-type:personal;
        font-family:"Calibri",sans-serif;
        color:windowtext;}
span.EmailStyle19
        {mso-style-type:personal;
        font-family:"Calibri",sans-serif;
        color:#1F497D;}
span.EmailStyle20
        {mso-style-type:personal;
        font-family:"Calibri",sans-serif;
        color:#1F497D;}
span.EmailStyle21
        {mso-style-type:personal;
        font-family:"Calibri",sans-serif;
        color:#1F497D;}
span.EmailStyle22
        {mso-style-type:personal;
        font-family:"Calibri",sans-serif;
        color:#1F497D;}
span.EmailStyle23
        {mso-style-type:personal-reply;
        font-family:"Calibri",sans-serif;
        color:#1F497D;}
.MsoChpDefault
        {mso-style-type:export-only;
        font-size:10.0pt;}
@page WordSection1
        {size:8.5in 11.0in;
        margin:1.0in 1.0in 1.0in 1.0in;}
div.WordSection1
        {page:WordSection1;}
/* List Definitions */
@list l0
        {mso-list-id:432092371;
        mso-list-type:hybrid;
        mso-list-template-ids:1382157516 67698705 67698713 67698715 67698703 67698713 67698715 67698703 67698713 67698715;}
@list l0:level1
        {mso-level-text:"%1\)";
        mso-level-tab-stop:none;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l0:level2
        {mso-level-number-format:alpha-lower;
        mso-level-tab-stop:none;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l0:level3
        {mso-level-number-format:roman-lower;
        mso-level-tab-stop:none;
        mso-level-number-position:right;
        text-indent:-9.0pt;}
@list l0:level4
        {mso-level-tab-stop:none;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l0:level5
        {mso-level-number-format:alpha-lower;
        mso-level-tab-stop:none;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l0:level6
        {mso-level-number-format:roman-lower;
        mso-level-tab-stop:none;
        mso-level-number-position:right;
        text-indent:-9.0pt;}
@list l0:level7
        {mso-level-tab-stop:none;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l0:level8
        {mso-level-number-format:alpha-lower;
        mso-level-tab-stop:none;
        mso-level-number-position:left;
        text-indent:-.25in;}
@list l0:level9
        {mso-level-number-format:roman-lower;
        mso-level-tab-stop:none;
        mso-level-number-position:right;
        text-indent:-9.0pt;}
ol
        {margin-bottom:0in;}
ul
        {margin-bottom:0in;}
--></style><!--[if gte mso 9]><xml>
<o:shapedefaults v:ext="edit" spidmax="1026" />
</xml><![endif]--><!--[if gte mso 9]><xml>
<o:shapelayout v:ext="edit">
<o:idmap v:ext="edit" data="1" />
</o:shapelayout></xml><![endif]-->
</head>
<body lang="EN-US" link="#0563C1" vlink="#954F72">
<div class="WordSection1">
<p class="MsoNormal"><span style="color:#1F497D">One of the reasons I&#8217;m following this closely is we&#8217;re seeing performance issues with security roles. We didn&#8217;t see anything in JIRA about that, so we&#8217;re in the process of characterizing it more and coming up
 with a foodmart example.<o:p></o:p></span></p>
<p class="MsoNormal"><span style="color:#1F497D"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal"><span style="color:#1F497D">--jeff<o:p></o:p></span></p>
<p class="MsoNormal"><span style="color:#1F497D"><o:p>&nbsp;</o:p></span></p>
<div>
<div style="border:none;border-top:solid #E1E1E1 1.0pt;padding:3.0pt 0in 0in 0in">
<p class="MsoNormal"><b>From:</b> mondrian-bounces@pentaho.org [mailto:mondrian-bounces@pentaho.org]
<b>On Behalf Of </b>Matt Campbell<br>
<b>Sent:</b> Friday, November 06, 2015 2:54 PM<br>
<b>To:</b> Mondrian developer mailing list &lt;mondrian@pentaho.org&gt;<br>
<b>Subject:</b> Re: [Mondrian] The perils of the crossjoin optimizer<o:p></o:p></p>
</div>
</div>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal"><span style="color:#1F497D">I guess the question is really whether to work on improving the cj optimizer, or to focus on making native evaluation better at handling queries like the grand-total one I pasted below.&nbsp; There&#8217;s a case for that--&nbsp;
 MONDRIAN-2426.&nbsp; If that could be handled in native eval we&#8217;d never hit the cj optimizer at all.<o:p></o:p></span></p>
<p class="MsoNormal"><span style="color:#1F497D"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal"><span style="color:#1F497D">Either way it seems sensible to me to have some number larger than 0 for a default cj optimizer threshold.&nbsp;
<o:p></o:p></span></p>
<p class="MsoNormal"><span style="color:#1F497D"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal"><span style="color:#1F497D"><o:p>&nbsp;</o:p></span></p>
<div>
<div style="border:none;border-top:solid #E1E1E1 1.0pt;padding:3.0pt 0in 0in 0in">
<p class="MsoNormal"><b>From:</b> <a href="mailto:mondrian-bounces@pentaho.org">mondrian-bounces@pentaho.org</a> [<a href="mailto:mondrian-bounces@pentaho.org">mailto:mondrian-bounces@pentaho.org</a>]
<b>On Behalf Of </b>Wright, Jeff<br>
<b>Sent:</b> Friday, November 06, 2015 2:20 PM<br>
<b>To:</b> Mondrian developer mailing list &lt;<a href="mailto:mondrian@pentaho.org">mondrian@pentaho.org</a>&gt;<br>
<b>Subject:</b> Re: [Mondrian] The perils of the crossjoin optimizer<o:p></o:p></p>
</div>
</div>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal"><span style="color:#1F497D">Thanks, that&#8217;s easier to follow. Your logic makes sense to me, that attempting to use a fact table to optimize axis evaluation makes more sense when you expect the tuple count to be high.<o:p></o:p></span></p>
<p class="MsoNormal"><span style="color:#1F497D"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal"><span style="color:#1F497D">Should we try to make the optimizer more efficient? Is that a trick question? I guess I&#8217;m curious if the calculated member issue would be different if it were an explicit slicer or a named set.<o:p></o:p></span></p>
<p class="MsoNormal"><span style="color:#1F497D"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal"><span style="color:#1F497D">--jeff<o:p></o:p></span></p>
<p class="MsoNormal"><span style="color:#1F497D"><o:p>&nbsp;</o:p></span></p>
<div>
<div style="border:none;border-top:solid #E1E1E1 1.0pt;padding:3.0pt 0in 0in 0in">
<p class="MsoNormal"><b>From:</b> <a href="mailto:mondrian-bounces@pentaho.org">mondrian-bounces@pentaho.org</a> [<a href="mailto:mondrian-bounces@pentaho.org">mailto:mondrian-bounces@pentaho.org</a>]
<b>On Behalf Of </b>Matt Campbell<br>
<b>Sent:</b> Friday, November 06, 2015 10:20 AM<br>
<b>To:</b> Mondrian developer mailing list &lt;<a href="mailto:mondrian@pentaho.org">mondrian@pentaho.org</a>&gt;<br>
<b>Subject:</b> Re: [Mondrian] The perils of the crossjoin optimizer<o:p></o:p></p>
</div>
</div>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal"><span style="color:#1F497D">Hi Jeff,<o:p></o:p></span></p>
<p class="MsoNormal"><span style="color:#1F497D"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal"><span style="color:#1F497D">Consider the crossjoin from my example:<o:p></o:p></span></p>
<p class="MsoNormal"><span style="color:#1F497D"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal"><span style="font-size:9.5pt;font-family:Consolas">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color:maroon">CrossJoin</span>(Store.[Store Total], Product.[Product Total]))<o:p></o:p></span></p>
<p class="MsoNormal"><span style="color:#1F497D"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal"><span style="color:#1F497D">It will not use native evaluation due to the presence of calculated members, so the crossjoin optimizer will pick it up.&nbsp; The optimizer will:<o:p></o:p></span></p>
<p class="MsoNormal"><span style="color:#1F497D"><o:p>&nbsp;</o:p></span></p>
<p class="MsoListParagraph" style="text-indent:-.25in;mso-list:l0 level1 lfo2"><![if !supportLists]><span style="color:#1F497D"><span style="mso-list:Ignore">1)<span style="font:7.0pt &quot;Times New Roman&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</span></span></span><![endif]><span style="color:#1F497D">Determine whether the size of the incoming tuple set exceeds the threshold (zero in this case).&nbsp; 1&gt;0, so yes it does.<o:p></o:p></span></p>
<p class="MsoListParagraph" style="text-indent:-.25in;mso-list:l0 level1 lfo2"><![if !supportLists]><span style="color:#1F497D"><span style="mso-list:Ignore">2)<span style="font:7.0pt &quot;Times New Roman&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</span></span></span><![endif]><span style="color:#1F497D">Determine all the base measures used in the query.<o:p></o:p></span></p>
<p class="MsoListParagraph" style="text-indent:-.25in;mso-list:l0 level1 lfo2"><![if !supportLists]><span style="color:#1F497D"><span style="mso-list:Ignore">3)<span style="font:7.0pt &quot;Times New Roman&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</span></span></span><![endif]><span style="color:#1F497D">Determine what slicer context can be applied.&nbsp; This will exclude the calculated member in the slicer, setting [Education Level] to [All].<o:p></o:p></span></p>
<p class="MsoListParagraph" style="text-indent:-.25in;mso-list:l0 level1 lfo2"><![if !supportLists]><span style="color:#1F497D"><span style="mso-list:Ignore">4)<span style="font:7.0pt &quot;Times New Roman&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</span></span></span><![endif]><span style="color:#1F497D">Iterate through the single input tuple, retrieving cell data for all base measures determined in (2) using the context from (3). This will require an unconstrained SQL query across the whole fact table
 (assuming the value wasn&#8217;t cached).<o:p></o:p></span></p>
<p class="MsoListParagraph" style="text-indent:-.25in;mso-list:l0 level1 lfo2"><![if !supportLists]><span style="color:#1F497D"><span style="mso-list:Ignore">5)<span style="font:7.0pt &quot;Times New Roman&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</span></span></span><![endif]><span style="color:#1F497D">Add only those tuples with data (i.e. the single tuple we started with) to the resulting set.<o:p></o:p></span></p>
<p class="MsoNormal"><span style="color:#1F497D"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal"><span style="color:#1F497D">All of this is part of axis evaluation, so reducing the tuple set means fewer intersections Mondrian has to evaluate downstream.&nbsp; In this case the set is just a single tuple, though, so limited value.<o:p></o:p></span></p>
<p class="MsoNormal"><span style="color:#1F497D"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal"><span style="color:#1F497D">As another example, consider<o:p></o:p></span></p>
<p class="MsoNormal"><span style="color:#1F497D"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal"><span style="font-size:9.5pt;font-family:Consolas;color:maroon">CrossJoin</span><span style="font-size:9.5pt;font-family:Consolas">([Customer].[Name].members, Product.[Product Total])</span><span style="color:#1F497D"><o:p></o:p></span></p>
<p class="MsoNormal"><span style="color:#1F497D"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal"><span style="color:#1F497D">We&#8217;ll again go through the same sequence of steps, but if there are 10s or 100s of thousands of customers the value of reducing the set size may be greater than the cost of the extra SQL executed.<o:p></o:p></span></p>
<p class="MsoNormal"><span style="color:#1F497D"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal"><span style="color:#1F497D">So I guess my question is <i>should the default threshold be higher</i>?&nbsp; I&#8217;m curious if others can think of realistic counter examples where it&#8217;s useful even with tiny set sizes.<o:p></o:p></span></p>
<p class="MsoNormal"><span style="color:#1F497D"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal"><span style="color:#1F497D">A side question is <i>should we try to make the optimizer more efficient</i>? &nbsp;&nbsp;I.e. can we make sure contraints from calculated members in the slicer get applied.
<o:p></o:p></span></p>
<p class="MsoNormal"><span style="color:#1F497D"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal"><span style="color:#1F497D"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal"><span style="color:#1F497D">-matt<o:p></o:p></span></p>
<p class="MsoNormal"><span style="color:#1F497D"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal"><span style="color:#1F497D"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal"><span style="color:#1F497D"><o:p>&nbsp;</o:p></span></p>
<div>
<div style="border:none;border-top:solid #E1E1E1 1.0pt;padding:3.0pt 0in 0in 0in">
<p class="MsoNormal"><b>From:</b> <a href="mailto:mondrian-bounces@pentaho.org">mondrian-bounces@pentaho.org</a> [<a href="mailto:mondrian-bounces@pentaho.org">mailto:mondrian-bounces@pentaho.org</a>]
<b>On Behalf Of </b>Wright, Jeff<br>
<b>Sent:</b> Friday, November 06, 2015 9:49 AM<br>
<b>To:</b> Mondrian developer mailing list &lt;<a href="mailto:mondrian@pentaho.org">mondrian@pentaho.org</a>&gt;<br>
<b>Subject:</b> Re: [Mondrian] The perils of the crossjoin optimizer<o:p></o:p></p>
</div>
</div>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal"><span style="color:#1F497D">I&#8217;ve read this several times and I&#8217;m just not following. I think you&#8217;re asking for feedback on a property setting that will control whether Mondrian uses the crossjoin optimizer or does something else. And it
 sounds like you&#8217;re looking for a way to make sure the slicer gets used in the cell loading sql.<o:p></o:p></span></p>
<p class="MsoNormal"><span style="color:#1F497D"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal"><span style="color:#1F497D">Could you maybe make a stab at describing the two query execution plans, and how this optimizer threshold comes into play?<o:p></o:p></span></p>
<p class="MsoNormal"><span style="color:#1F497D"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal"><span style="color:#1F497D">--jeff<o:p></o:p></span></p>
<p class="MsoNormal"><span style="color:#1F497D"><o:p>&nbsp;</o:p></span></p>
<div>
<div style="border:none;border-top:solid #E1E1E1 1.0pt;padding:3.0pt 0in 0in 0in">
<p class="MsoNormal"><b>From:</b> <a href="mailto:mondrian-bounces@pentaho.org">mondrian-bounces@pentaho.org</a> [<a href="mailto:mondrian-bounces@pentaho.org">mailto:mondrian-bounces@pentaho.org</a>]
<b>On Behalf Of </b>Matt Campbell<br>
<b>Sent:</b> Friday, November 06, 2015 9:19 AM<br>
<b>To:</b> Mondrian developer mailing list &lt;<a href="mailto:mondrian@pentaho.org">mondrian@pentaho.org</a>&gt;<br>
<b>Subject:</b> [Mondrian] The perils of the crossjoin optimizer<o:p></o:p></p>
</div>
</div>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">Mondrian&#8217;s crossjoin optimizer acts as a fall back to native crossjoin, applying an alternative optimization strategy in cases where native evaluation was not possible or disabled.&nbsp; It works by loading cell data for crossjoined tuples to
 eliminate empty intersections.&nbsp; Loading these cells can be an added cost, but often that&#8217;s okay since the cells may have been needed anyway, if not by this query then potentially by similar queries.<o:p></o:p></p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">There are scenarios where the cost can be excessive, however.&nbsp; The MDX below loads detail rows with a grand total.&nbsp; In this case, the second crossjoin (of two &#8220;Total&#8221; calculated members) cannot be natively evaluated, so the crossjoin optimizer
 gets a shot.&nbsp; Since the WHERE slicer is also a calculated member, however, this results in an unconstrained SQL query against the fact table.&nbsp; That a big expense to reduce a tuple set that&#8217;s already of size 1.<o:p></o:p></p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">The default crossjoin optimizer threshold has a value of 0 tuples, meaning it always kicks in.&nbsp; I&#8217;m not sure what the ideal default is, but it seems to me that in most cases with tiny sets the risk of expensive SQL outweighs the benefit.&nbsp;
 A setting somewhere in the 10-100 region would eliminate many SQL queries with total/subtotal MDX like the one below.<i><o:p></o:p></i></p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal">Thoughts?<o:p></o:p></p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal" style="text-autospace:none"><span style="font-size:9.5pt;font-family:Consolas;color:blue">WITH</span><span style="font-size:9.5pt;font-family:Consolas"><o:p></o:p></span></p>
<p class="MsoNormal" style="text-autospace:none"><span style="font-size:9.5pt;font-family:Consolas;color:blue">member</span><span style="font-size:9.5pt;font-family:Consolas"> Store.[Store Total]
<span style="color:blue">as</span> <o:p></o:p></span></p>
<p class="MsoNormal" style="text-autospace:none"><span style="font-size:9.5pt;font-family:Consolas">&nbsp;&nbsp;&nbsp;&nbsp;<span style="color:#A31515">'Aggregate([Store].[Store State].[WA].children)'</span><o:p></o:p></span></p>
<p class="MsoNormal" style="text-autospace:none"><span style="font-size:9.5pt;font-family:Consolas;color:blue">member</span><span style="font-size:9.5pt;font-family:Consolas"> Product.[Product Total]
<span style="color:blue">as</span> <o:p></o:p></span></p>
<p class="MsoNormal" style="text-autospace:none"><span style="font-size:9.5pt;font-family:Consolas">&nbsp;&nbsp;&nbsp;&nbsp;<span style="color:#A31515">'Aggregate({[Product].[All Products].[Drink], [Product].[All Products].[Food]})'</span><o:p></o:p></span></p>
<p class="MsoNormal" style="text-autospace:none"><span style="font-size:9.5pt;font-family:Consolas;color:blue">member</span><span style="font-size:9.5pt;font-family:Consolas"> [Education Level].[Education Filter]
<span style="color:blue">as</span> <o:p></o:p></span></p>
<p class="MsoNormal" style="text-autospace:none"><span style="font-size:9.5pt;font-family:Consolas">&nbsp;&nbsp;&nbsp;&nbsp;<span style="color:#A31515">'Aggregate({[Education Level].[All Education Level].[Bachelors Degree],</span><o:p></o:p></span></p>
<p class="MsoNormal" style="text-autospace:none"><span style="font-size:9.5pt;font-family:Consolas;color:#A31515">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [Education Level].[All Education Level].[Graduate Degree]})'</span><span style="font-size:9.5pt;font-family:Consolas"><o:p></o:p></span></p>
<p class="MsoNormal" style="text-autospace:none"><span style="font-size:9.5pt;font-family:Consolas"><o:p>&nbsp;</o:p></span></p>
<p class="MsoNormal" style="text-autospace:none"><span style="font-size:9.5pt;font-family:Consolas;color:blue">SELECT</span><span style="font-size:9.5pt;font-family:Consolas"> Measures.[Unit Sales]
<span style="color:blue">on</span> 0,<o:p></o:p></span></p>
<p class="MsoNormal" style="text-autospace:none"><span style="font-size:9.5pt;font-family:Consolas">&nbsp;
<span style="color:blue">NON</span> <span style="color:blue">EMPTY</span> <o:p></o:p></span></p>
<p class="MsoNormal" style="text-autospace:none"><span style="font-size:9.5pt;font-family:Consolas">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color:blue">UNION</span>(<o:p></o:p></span></p>
<p class="MsoNormal" style="text-autospace:none"><span style="font-size:9.5pt;font-family:Consolas">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<span style="color:maroon">CrossJoin</span>([Store].[Store State].[WA].<span style="color:maroon">children</span>,
<o:p></o:p></span></p>
<p class="MsoNormal" style="text-autospace:none"><span style="font-size:9.5pt;font-family:Consolas">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{[Product].[All Products].[Drink], [Product].[All Products].[Food]}),&nbsp;&nbsp;
<o:p></o:p></span></p>
<p class="MsoNormal" style="text-autospace:none"><span style="font-size:9.5pt;font-family:Consolas">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color:maroon">CrossJoin</span>(Store.[Store Total], Product.[Product Total]))
<span style="color:blue">on</span> 1<o:p></o:p></span></p>
<p class="MsoNormal" style="text-autospace:none"><span style="font-size:9.5pt;font-family:Consolas;color:blue">FROM</span><span style="font-size:9.5pt;font-family:Consolas"> Sales<o:p></o:p></span></p>
<p class="MsoNormal" style="text-autospace:none"><span style="font-size:9.5pt;font-family:Consolas;color:blue">WHERE</span><span style="font-size:9.5pt;font-family:Consolas"><o:p></o:p></span></p>
<p class="MsoNormal" style="text-autospace:none"><span style="font-size:9.5pt;font-family:Consolas">&nbsp;&nbsp; [Education Level].[Education Filter]<o:p></o:p></span></p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
</div>
</body>
</html>