You are on page 1of 38

Anatomy of the DAX Query Plan

Stacia Varga
Data Inspirations
stacia@datainspiratons.com
blog.datainspirations.com
@_Stacia_V

12.12.2015
12.12.2015
Thank you to our AWESOME sponsors!

12.12.2015
Stacia (Misner) Varga

Over 30 years of IT experience, 17 years of BI


experience
Frequently speaks, writes, and teaches about
Microsoft BI technologies

Principal Consultant and Founder,


Data Inspirations

Data Platform MVP and SSAS Maestro

Las Vegas User Group Chapter Leader and SQL


Saturday Organizer
Wants you to make sure you can understand
what y our data is telling y ou

12.12.2015
Agenda

Introducing Query Architecture


Reviewing Logical versus Physical Plans
Understanding Key Elements of a Plan

12.12.2015
INTRODUCING QUERY ARCHITECTURE

12.12.2015
Formula Engine

Evaluates DAX functions


Assembles results
Single-threaded

12.12.2015
Storage Engine

Evaluates simple mathematical operations


Manages database queries
Multi-threaded

12.12.2015
Optimization Goals

Maximize storage engine processing


Minimize formula engine processing

12.12.2015
REVIEWING LOGICAL VERSUS PHYSICAL PLANS

12.12.2015
Query Plans
Logical Physical
Execution tree used by Engine execution of query
physical plan Listing of operators and
Insight into physical plan parameters used during
for complex queries execution

12.12.2015
Tools for Query Plans

SQL Server Profiler


DAX Studio
Extended Events

12.12.2015
Simple Query

12.12.2015
Profiler Events

12.12.2015
Profiler Logical Plan

GroupBy_Vertipaq: RelLogOp DependOnCols()() 0-1


RequiredCols(0, 1)('Date'[Calendar Year], 'Reseller
Sales'[Sales Amount]) Table='Reseller Sales' -BlankRow
Scan_Vertipaq: RelLogOp DependOnCols()() 0-161
RequiredCols(15, 153)('Date'[Calendar Year], 'Reseller
Sales'[Sales Amount]) Table='Reseller Sales' -BlankRow

12.12.2015
Profiler Physical Plan

Spool_Iterator<Spool>: IterPhyOp IterCols(0,


1)('Date'[Calendar Year], 'Reseller Sales'[Sales Amount])
#Records=2328 #KeyCols=238 #ValueCols=0
AggregationSpool<Cache>: SpoolPhyOp #Records=2328
VertipaqResult: IterPhyOp #FieldCols=2 #ValueCols=0

12.12.2015
UNDERSTANDING KEY ELEMENTS OF A PLAN

12.12.2015
Profiler Logical Plan

Parent Operator Node

GroupBy_Vertipaq: RelLogOp DependOnCols()() 0-1


RequiredCols(0, 1)('Date'[Calendar Year], 'Reseller
Sales'[Sales Amount]) Table='Reseller Sales' -BlankRow
Scan_Vertipaq: RelLogOp DependOnCols()() 0-161
RequiredCols(15, 153)('Date'[Calendar Year], 'Reseller
Sales'[Sales Amount]) Table='Reseller Sales' -BlankRow

12.12.2015
Profiler Logical Plan

GroupBy_Vertipaq: RelLogOp DependOnCols()() 0-1


RequiredCols(0, 1)('Date'[Calendar Year], 'Reseller
Child Operator Node
Sales'[Sales Amount]) Table='Reseller Sales' -BlankRow
Scan_Vertipaq: RelLogOp DependOnCols()() 0-161
RequiredCols(15, 153)('Date'[Calendar Year], 'Reseller
Sales'[Sales Amount]) Table='Reseller Sales' -BlankRow

12.12.2015
Profiler Logical Plan
Operator
GroupBy_Vertipaq: RelLogOp DependOnCols()() 0-1
RequiredCols(0, 1)('Date'[Calendar Year], 'Reseller
Sales'[Sales Amount]) Table='Reseller Sales' -BlankRow
Scan_Vertipaq: RelLogOp DependOnCols()() 0-161
RequiredCols(15, 153)('Date'[Calendar Year], 'Reseller
Sales'[Sales Amount]) Table='Reseller Sales' -BlankRow

12.12.2015
Profiler Logical Plan
Operator Properties
GroupBy_Vertipaq: RelLogOp DependOnCols()() 0-1
RequiredCols(0, 1)('Date'[Calendar Year], 'Reseller
Sales'[Sales Amount]) Table='Reseller Sales' -BlankRow
Scan_Vertipaq: RelLogOp DependOnCols()() 0-161
RequiredCols(15, 153)('Date'[Calendar Year], 'Reseller
Sales'[Sales Amount]) Table='Reseller Sales' -BlankRow

12.12.2015
Profiler Logical Plan
Operator Type

GroupBy_Vertipaq: RelLogOp DependOnCols()() 0-1


RequiredCols(0, 1)('Date'[Calendar Year], 'Reseller
Sales'[Sales Amount]) Table='Reseller Sales' -BlankRow
Scan_Vertipaq: RelLogOp DependOnCols()() 0-161
RequiredCols(15, 153)('Date'[Calendar Year], 'Reseller
Sales'[Sales Amount]) Table='Reseller Sales' -BlankRow

12.12.2015
Column Lists

Column Number lists + Column Name lists


DependOnCols()()
RequiredCols(0, 1) ('Date'[Calendar
Year], 'Reseller Sales'[Sales
Amount])
These two lists are
always shown
empty or not

12.12.2015
Column Lists

Column Number lists + Column Name lists


LookupCols(0)('Date'[Calendar Year])
IterCols(0) ('Date'[Calendar Year])

These two lists show only


when not empty

12.12.2015
Operator Types

ScaLogOp
RelLogOp
LookupPhyOp
IterPhyOp

12.12.2015
ScalLogOp Properties

DependOnCols
Useful for confirming measures have correct row context
Data Type
DAX data type or BLANK
DominantValue
NONE is dense, else sparse
Sparse is like block mode in MDX (fast)

12.12.2015
RelLogOp Properties

DependOnCols
Useful for confirming measures have correct row context
Range of Column Numbers
Minimal set of continuous columns to resolve query
RequiredCols
Selection from range and DependOnCols to resolve query

12.12.2015
LookupPhyOp Properties

LookupCols
Columns from an iterator to produce scalar value
Data type
DAX data type or BLANK

12.12.2015
IterPhyOp Properties

LookupCols
Input columns from an iterator to send to another iterator
IterCols
Output columns

12.12.2015
Vertipaq Operators

Scan_Vertipaq
Groupby_Vertipaq
VertipaqResult

12.12.2015
Scan_Vertipaq (Logical)
Scan_Vertipaq: RelLogOp DependOnCols()() 0-161
RequiredCols(15, 153)('Date'[Calendar Year], 'Reseller
Sales'[Sales Amount]) Table='Reseller Sales' -BlankRow

Joins root table to tables w/many-to-one relationships


Groups by output columns

12.12.2015
More Properties
Table
Root table
Optional blank row for referential integrity issues
(+ include, - exclude)
JoinCols
Subset of DependOnCols used for join to other tables
SemiJoinCols
Not always present
Signifies FE may fetch more columns for joining and
removing these columns afterwards (expensive operation)

12.12.2015
Groupby_Vertipaq (Logical)
GroupBy_Vertipaq: RelLogOp DependOnCols()() 0-1
RequiredCols(0, 1)('Date'[Calendar Year], 'Reseller
Sales'[Sales Amount]) Table='Reseller Sales' -BlankRow

Renames columns
Adds rollup columns to Vertipaq query

12.12.2015
VertipaqResult (Physical)
VertipaqResult: IterPhyOp #FieldCols=2 #ValueCols=0

Iterates over result set

12.12.2015
Spools (Physical)
Spool_Iterator<Spool>: IterPhyOp IterCols(0,
1)('Date'[Calendar Year], 'Reseller Sales'[Sales Amount])
#Records=2328 #KeyCols=238 #ValueCols=0
AggregationSpool<Cache>: SpoolPhyOp #Records=2328

Spooling saves results for subsequent execution


Observe the number of records per spool
Exponentially higher values can result in performance degradation
More records spooled = more memory consumed

12.12.2015
CallBackDataID

Storage Engine operator


Triggers computation of complex expression during
table scan
Check to see if spooling required (more memory) or row-by-
row iteration (less memory)

12.12.2015
Remember

Optimize for Storage Engine

Review Logical and physical plans for clues

12.12.2015
To Learn More

Understanding DAX Query Plans (Russo, Ferrari)


https://www.sqlbi.com/articles/understanding-dax-query-
plans/
DAX Query Plan series (Wang)
http://mdxdax.blogspot.si/2011/12/dax-query-plan-part-1-
introduction.html
http://mdxdax.blogspot.si/2012/01/dax-query-plan-part-2-
operator.html
http://mdxdax.blogspot.si/2012/03/dax-query-plan-part-3-
vertipaq.html

12.12.2015
To Learn More

Introduction to Analysis Services Extended Events


(Vaillancourt)
http://markvsql.com/2014/02/introduction-to-analysis-
services-extended-events/

12.12.2015