In writing some sample demos around LOB and Row-Overflow data I found a couple of oddities in the way reads are reported in STATISTICS IO and query plans, There is every chance I’m missing something obvious here and if that is the case then please let me know as I’d love to understand this more.
The following is what I have observed on SQL Server 2017…
- If I turn on Statistics IO and query a table with no LOB allocations but some Row-Overflow allocations I see LOB logical reads occurring
- If I run that same query and look at the actual execution plan under Actual I/O Statistics no LOB reads are reported
- If I create a new table with a column that will be put in LOB storage the query plan still reports zero LOB logical reads even though STATISTICS IO returns a number greater than zero.
Let’s look at these examples…
Setup
First lets create a sandbox database…
Then lets add a little stored procedure we can run throughout our examples to see the allocation units…
In-Row Demo
Before we look at any Row-Overflow or LOB allocation units lets take this simple example where all the data fits In-Row.
Then lets check out GetAllocationUnits procedure…
We can see we have a single allocation unit for In-Row pages on this table. If we now run a SELECT * with statistics IO turned on we should see a single logical read…
Bingo.
Overflow-Row Demo
Let’s now create a table with no fields that qualify for LOB but enough variable length for us to cause overflows…
If we then run our GetAllocationUnits procedure we should see that we have some data in row and some in overflow due to the fact we can’t fit our 100 length field1 and our 8000 length field2 on a single page…
If we then run a select * we’d expect to have to read at least 2 pages, one from the In-Row allocation unit and one from our Overflow-Row allocation unit…
I’m still not sure about this reporting as a LOB read when it’s really Row-Overflow but I guess it is what it is. What I really find odd is that if we run the above query again but turn on actual query plans we see no LOB reads…
LOB Data Demo
Where this gets even weirder is if we then create another table that has real LOB data…
Now let’s do select * again…
As expected we can see our LOB reads however if we switch back to our actual execution plan…
Still now LOB reads showing under Actual I/O Statistics.
I’ve even tried running the insert statement multiple times to increase the page count, STATISTICS IO ON correctly reports the read pages but my actual execution plan stays the same with zero LOB pages read. Weird Right?