I am well aware what a forwarded record within a heap is. Since I want to keep forwarded records at 0, we decided to update only on columns that could not be extended.
Recently on my system I encountered forwarded records.
Table design is like this:
CREATE TABLE dbo.test (
HashValue BINARY(16) NOT NULL,
LoadTime DATETIME NOT NULL,
LoadEndTime DATETIME NULL,
[other columns that never get updates]
) WITH(DATA_COMPRESSION=PAGE);
The insert statements ALWAYS bring HashValue
AND LoadTime
. I checked the query logs.
I insert a value of '9999-12-31'.
Now system performs an update on LoadTime
like this:
;WITH CTE AS (
SELECT *, COALESCE(LEAD(LoadTime) OVER(PARTITION BY HashValue ORDER BY LoadTime) ,'9999-12-31') as EndTimeStamp
)
UPDATE CTE SET LoadEndTime = EndTimeStamp;
Since the LoadEndTime
column is always filled, there should be no extension of that column within the row when the update is executed. It should be an in place update. Still I get forwarded records always after that process… It doesn't make sense to me.
Insert Statement is like this:
INSERT INTO dbo.test (HashValue, LoadTime,LoadEndTime)
SELECT HASHBYTES(...), GETDATE(), '1900-01-01'
So there is already a dummy value. Even with compression the fixed and variable representation of datetime '1900-01-01 00:00:00' should be 8 bytes.
Best Answer
This is due to compression.
Compressed table has no more
FixedVar
row format, so even if you use fixed-length columns they aren't stored in Fixedvar way, no more fixed space reserved for (evenNULL
) values.When you use page compression, frist ROW COMPRESSION is applied. And here what we have:Row Compression Implementation
Here is my repro where I use dbo.Nums, table of 1000000 natural numbers, to fill my two tables:
dbo.test_comp
with page compression enabled anddbo.test_no_comp
with no compression:UPDATE
This is completely untrue, because you use
PAGE
compression that creates a dictionary and substitute repeating values:But I wanted point out another thing. Did you test if your PAGE compression make sense at all in your case? You have a heap and it's not static, i.e. you update it.
So in your case even if you can achieve compression on INSERT (only if you use bulk insert with
tablock
), any update will only destroy your compression.Look at my new test.
I realized that in my first test when i did INSERT without tablock, only row compression was applied, I inserted 1000000 rows and I get 19688 pages. Now I insert with tablock into the same table:
Now I have only 1387 pages. 1387 vs 19688 only because in first case page compression, even if defined, was not applied since my INSERT was without tablock
And now I update my nice compressed table:
Wow, after update I have 22422 pages vs 1387 original pages!
After this test I really NEVER will use page compression on a heap that is not static. The final table size is not much less compared with its non-compressed analog but as a bonus I have 990247 forwarded records out of 1000000