Anatomy of the DAX Query Plan
Stacia Varga
Data Inspirations
[email protected]
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