Sparse columns, cpu time & filtered indexesCheck existence with EXISTS outperform COUNT! … Not?Which of these queries is best for performance?Improve query performance when selecting almost all rows with many “group by” columnsSQL Server - Logical Reads lowered, Execution time remained the sameMulti-statement TVF vs Inline TVF PerformanceDoes IMAGE column affect query performance even if it's not included in the query?Aggregation in Outer Apply vs Left Join vs Derived tableHigh processor utilization when running a stored procedurelogical reads on global temp table, but not on session-level temp table
Worms crawling under skin
My 15 year old son is gay. How do I express my feelings about this?
Hilbert's hotel, why can't I repeat it infinitely many times?
Should I complain to HR about being mocked for request I made
Find missing number in the transformation
What was the deeper meaning of Hermione wanting the cloak?
Resolving moral conflict
Safely hang a mirror that does not have hooks
Does the Orange League not count as an official Pokemon League, making the Alolan League his first-ever win?
How to ask a man to not take up more than one seat on public transport while avoiding conflict?
Do you need to hold concentration on a spell when you cast it with a spell scroll?
What is the lowest voltage that a microcontroller can successfully read on the analog pin?
An Algorithm Which Schedules Your Life
Did Apollo carry and use WD40?
How to manage expenditure when billing cycles and paycheck cycles are not aligned?
How to make interviewee comfortable interviewing in lounge chairs
What is the meaning of "heutig" in this sentence?
As an employer, can I compel my employees to vote?
Is it right to extend flaps only in the white arc?
Can this word order be rearranged?
Do the villains know Batman has no superpowers?
Guitar tuning (EADGBE), "perfect" fourths?
Is there a scenario where a gnoll flesh gnawer can move at least 45 feet during its Rampage bonus action?
A high quality contribution but an annoying error is present in my published article
Sparse columns, cpu time & filtered indexes
Check existence with EXISTS outperform COUNT! … Not?Which of these queries is best for performance?Improve query performance when selecting almost all rows with many “group by” columnsSQL Server - Logical Reads lowered, Execution time remained the sameMulti-statement TVF vs Inline TVF PerformanceDoes IMAGE column affect query performance even if it's not included in the query?Aggregation in Outer Apply vs Left Join vs Derived tableHigh processor utilization when running a stored procedurelogical reads on global temp table, but not on session-level temp table
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty margin-bottom:0;
Sparsing
When doing some tests on sparse columns, as you do, there was a performance setback that I would like to know the direct cause of.
DDL
I created two identical tables, one with 4 sparse columns and one with no sparse columns.
--Non Sparse columns table & NC index
CREATE TABLE dbo.nonsparse( ID INT IDENTITY(1,1) PRIMARY KEY NOT NULL,
charval char(20) NULL,
varcharval varchar(20) NULL,
intval int NULL,
bigintval bigint NULL
);
CREATE INDEX IX_Nonsparse_intval_varcharval
ON dbo.nonsparse(intval,varcharval)
INCLUDE(bigintval,charval);
-- sparse columns table & NC index
CREATE TABLE dbo.sparse( ID INT IDENTITY(1,1) PRIMARY KEY NOT NULL,
charval char(20) SPARSE NULL ,
varcharval varchar(20) SPARSE NULL,
intval int SPARSE NULL,
bigintval bigint SPARSE NULL
);
CREATE INDEX IX_sparse_intval_varcharval
ON dbo.sparse(intval,varcharval)
INCLUDE(bigintval,charval);
DML
I then inserted about 2540 NON-NULL values into both.
INSERT INTO dbo.nonsparse WITH(TABLOCK) (charval, varcharval,intval,bigintval)
SELECT 'Val1','Val2',20,19
FROM MASTER..spt_values;
INSERT INTO dbo.sparse WITH(TABLOCK) (charval, varcharval,intval,bigintval)
SELECT 'Val1','Val2',20,19
FROM MASTER..spt_values;
Afterwards, I inserted 1M NULL values into both tables
INSERT INTO dbo.nonsparse WITH(TABLOCK) (charval, varcharval,intval,bigintval)
SELECT TOP(1000000) NULL,NULL,NULL,NULL
FROM MASTER..spt_values spt1
CROSS APPLY MASTER..spt_values spt2;
INSERT INTO dbo.sparse WITH(TABLOCK) (charval, varcharval,intval,bigintval)
SELECT TOP(1000000) NULL,NULL,NULL,NULL
FROM MASTER..spt_values spt1
CROSS APPLY MASTER..spt_values spt2;
Queries
Nonsparse table execution
When running this query twice on the newly created nonsparse table:
SET STATISTICS IO, TIME ON;
SELECT * FROM dbo.nonsparse
WHERE 1= (SELECT 1) -- force non trivial plan
OPTION(RECOMPILE,MAXDOP 1);
The logical reads show 5257 pages
(1002540 rows affected)
Table 'nonsparse'. Scan count 1, logical reads 5257, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
And the cpu time is at 343 ms
SQL Server Execution Times:
CPU time = 343 ms, elapsed time = 3850 ms.
sparse table execution
Running the same query twice on the sparse table:
SELECT * FROM dbo.sparse
WHERE 1= (SELECT 1) -- force non trivial plan
OPTION(RECOMPILE,MAXDOP 1);
The reads are lower, 1763
(1002540 rows affected)
Table 'sparse'. Scan count 1, logical reads 1763, physical reads 3, read-ahead reads 1759, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
But the cpu time is higher, 547 ms.
SQL Server Execution Times:
CPU time = 547 ms, elapsed time = 2406 ms.
Sparse table execution plan
non sparse table execution plan
Questions
Original question
Since the NULL values are not stored directly in the sparse columns, could the increase in cpu time be due to returning the NULL values as a resultset? Or is it simply the behaviour as noted in the documentation?
Sparse columns reduce the space requirements for null values at the
cost of more overhead to retrieve nonnull values
Or is the overhead only related to reads & storage used?
Even when running ssms with the discard results after execution option, the cpu time of the sparse select was higher (407 ms) in comparison to the non sparse (219 ms).
EDIT
It might have been the overhead of the non null values, even if there are only 2540 present, but I am still not convinced.
This seems to be about the same performance, but the sparse factor was lost.
CREATE INDEX IX_Filtered
ON dbo.sparse(charval,varcharval,intval,bigintval)
WHERE charval IS NULL
AND varcharval IS NULL
AND intval IS NULL
AND bigintval IS NULL;
CREATE INDEX IX_Filtered
ON dbo.nonsparse(charval,varcharval,intval,bigintval)
WHERE charval IS NULL
AND varcharval IS NULL
AND intval IS NULL
AND bigintval IS NULL;
SET STATISTICS IO, TIME ON;
SELECT charval,varcharval,intval,bigintval FROM dbo.sparse WITH(INDEX(IX_Filtered))
WHERE charval IS NULL AND varcharval IS NULL
AND intval IS NULL
AND bigintval IS NULL
OPTION(RECOMPILE,MAXDOP 1);
SELECT charval,varcharval,intval,bigintval
FROM dbo.nonsparse WITH(INDEX(IX_Filtered))
WHERE charval IS NULL AND
varcharval IS NULL
AND intval IS NULL
AND bigintval IS NULL
OPTION(RECOMPILE,MAXDOP 1);
Seems to have about the same execution time:
SQL Server Execution Times:
CPU time = 297 ms, elapsed time = 292 ms.
SQL Server Execution Times:
CPU time = 281 ms, elapsed time = 319 ms.
But why are the logical reads the same amount now? Shouldn't the filtered index for the sparse column not store anything except the included ID field and some other non-data pages?
Table 'sparse'. Scan count 1, logical reads 5785,
Table 'nonsparse'. Scan count 1, logical reads 5785
And the size of both indices:
RowCounts Used_MB Unused_MB Total_MB
1000000 45.20 0.06 45.26
Why are these the same size? Was the sparse-ness lost?
Both query plans when using the filtered index
Extra Info
select @@version
Microsoft SQL Server 2017 (RTM-CU16) (KB4508218) -
14.0.3223.3 (X64) Jul 12 2019 17:43:08 Copyright (C) 2017 Microsoft Corporation Developer Edition (64-bit) on Windows Server
2012 R2 Datacenter 6.3 (Build 9600: ) (Hypervisor)
While running the queries and only selecting the ID field, the cpu time is comparable, with lower logical reads for the sparse table.
Size of the tables
SchemaName TableName RowCounts Used_MB Unused_MB Total_MB
dbo nonsparse 1002540 89.54 0.10 89.64
dbo sparse 1002540 27.95 0.20 28.14
When forcing either the clustered or nonclustered index, the cpu time difference remains.
sql-server sql-server-2017 sparse-column
add a comment
|
Sparsing
When doing some tests on sparse columns, as you do, there was a performance setback that I would like to know the direct cause of.
DDL
I created two identical tables, one with 4 sparse columns and one with no sparse columns.
--Non Sparse columns table & NC index
CREATE TABLE dbo.nonsparse( ID INT IDENTITY(1,1) PRIMARY KEY NOT NULL,
charval char(20) NULL,
varcharval varchar(20) NULL,
intval int NULL,
bigintval bigint NULL
);
CREATE INDEX IX_Nonsparse_intval_varcharval
ON dbo.nonsparse(intval,varcharval)
INCLUDE(bigintval,charval);
-- sparse columns table & NC index
CREATE TABLE dbo.sparse( ID INT IDENTITY(1,1) PRIMARY KEY NOT NULL,
charval char(20) SPARSE NULL ,
varcharval varchar(20) SPARSE NULL,
intval int SPARSE NULL,
bigintval bigint SPARSE NULL
);
CREATE INDEX IX_sparse_intval_varcharval
ON dbo.sparse(intval,varcharval)
INCLUDE(bigintval,charval);
DML
I then inserted about 2540 NON-NULL values into both.
INSERT INTO dbo.nonsparse WITH(TABLOCK) (charval, varcharval,intval,bigintval)
SELECT 'Val1','Val2',20,19
FROM MASTER..spt_values;
INSERT INTO dbo.sparse WITH(TABLOCK) (charval, varcharval,intval,bigintval)
SELECT 'Val1','Val2',20,19
FROM MASTER..spt_values;
Afterwards, I inserted 1M NULL values into both tables
INSERT INTO dbo.nonsparse WITH(TABLOCK) (charval, varcharval,intval,bigintval)
SELECT TOP(1000000) NULL,NULL,NULL,NULL
FROM MASTER..spt_values spt1
CROSS APPLY MASTER..spt_values spt2;
INSERT INTO dbo.sparse WITH(TABLOCK) (charval, varcharval,intval,bigintval)
SELECT TOP(1000000) NULL,NULL,NULL,NULL
FROM MASTER..spt_values spt1
CROSS APPLY MASTER..spt_values spt2;
Queries
Nonsparse table execution
When running this query twice on the newly created nonsparse table:
SET STATISTICS IO, TIME ON;
SELECT * FROM dbo.nonsparse
WHERE 1= (SELECT 1) -- force non trivial plan
OPTION(RECOMPILE,MAXDOP 1);
The logical reads show 5257 pages
(1002540 rows affected)
Table 'nonsparse'. Scan count 1, logical reads 5257, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
And the cpu time is at 343 ms
SQL Server Execution Times:
CPU time = 343 ms, elapsed time = 3850 ms.
sparse table execution
Running the same query twice on the sparse table:
SELECT * FROM dbo.sparse
WHERE 1= (SELECT 1) -- force non trivial plan
OPTION(RECOMPILE,MAXDOP 1);
The reads are lower, 1763
(1002540 rows affected)
Table 'sparse'. Scan count 1, logical reads 1763, physical reads 3, read-ahead reads 1759, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
But the cpu time is higher, 547 ms.
SQL Server Execution Times:
CPU time = 547 ms, elapsed time = 2406 ms.
Sparse table execution plan
non sparse table execution plan
Questions
Original question
Since the NULL values are not stored directly in the sparse columns, could the increase in cpu time be due to returning the NULL values as a resultset? Or is it simply the behaviour as noted in the documentation?
Sparse columns reduce the space requirements for null values at the
cost of more overhead to retrieve nonnull values
Or is the overhead only related to reads & storage used?
Even when running ssms with the discard results after execution option, the cpu time of the sparse select was higher (407 ms) in comparison to the non sparse (219 ms).
EDIT
It might have been the overhead of the non null values, even if there are only 2540 present, but I am still not convinced.
This seems to be about the same performance, but the sparse factor was lost.
CREATE INDEX IX_Filtered
ON dbo.sparse(charval,varcharval,intval,bigintval)
WHERE charval IS NULL
AND varcharval IS NULL
AND intval IS NULL
AND bigintval IS NULL;
CREATE INDEX IX_Filtered
ON dbo.nonsparse(charval,varcharval,intval,bigintval)
WHERE charval IS NULL
AND varcharval IS NULL
AND intval IS NULL
AND bigintval IS NULL;
SET STATISTICS IO, TIME ON;
SELECT charval,varcharval,intval,bigintval FROM dbo.sparse WITH(INDEX(IX_Filtered))
WHERE charval IS NULL AND varcharval IS NULL
AND intval IS NULL
AND bigintval IS NULL
OPTION(RECOMPILE,MAXDOP 1);
SELECT charval,varcharval,intval,bigintval
FROM dbo.nonsparse WITH(INDEX(IX_Filtered))
WHERE charval IS NULL AND
varcharval IS NULL
AND intval IS NULL
AND bigintval IS NULL
OPTION(RECOMPILE,MAXDOP 1);
Seems to have about the same execution time:
SQL Server Execution Times:
CPU time = 297 ms, elapsed time = 292 ms.
SQL Server Execution Times:
CPU time = 281 ms, elapsed time = 319 ms.
But why are the logical reads the same amount now? Shouldn't the filtered index for the sparse column not store anything except the included ID field and some other non-data pages?
Table 'sparse'. Scan count 1, logical reads 5785,
Table 'nonsparse'. Scan count 1, logical reads 5785
And the size of both indices:
RowCounts Used_MB Unused_MB Total_MB
1000000 45.20 0.06 45.26
Why are these the same size? Was the sparse-ness lost?
Both query plans when using the filtered index
Extra Info
select @@version
Microsoft SQL Server 2017 (RTM-CU16) (KB4508218) -
14.0.3223.3 (X64) Jul 12 2019 17:43:08 Copyright (C) 2017 Microsoft Corporation Developer Edition (64-bit) on Windows Server
2012 R2 Datacenter 6.3 (Build 9600: ) (Hypervisor)
While running the queries and only selecting the ID field, the cpu time is comparable, with lower logical reads for the sparse table.
Size of the tables
SchemaName TableName RowCounts Used_MB Unused_MB Total_MB
dbo nonsparse 1002540 89.54 0.10 89.64
dbo sparse 1002540 27.95 0.20 28.14
When forcing either the clustered or nonclustered index, the cpu time difference remains.
sql-server sql-server-2017 sparse-column
1
Could you get the plans for the post-edit query?
– George.Palacios
7 hours ago
1
@George.Palacios added them :)
– Randi Vertongen
6 hours ago
add a comment
|
Sparsing
When doing some tests on sparse columns, as you do, there was a performance setback that I would like to know the direct cause of.
DDL
I created two identical tables, one with 4 sparse columns and one with no sparse columns.
--Non Sparse columns table & NC index
CREATE TABLE dbo.nonsparse( ID INT IDENTITY(1,1) PRIMARY KEY NOT NULL,
charval char(20) NULL,
varcharval varchar(20) NULL,
intval int NULL,
bigintval bigint NULL
);
CREATE INDEX IX_Nonsparse_intval_varcharval
ON dbo.nonsparse(intval,varcharval)
INCLUDE(bigintval,charval);
-- sparse columns table & NC index
CREATE TABLE dbo.sparse( ID INT IDENTITY(1,1) PRIMARY KEY NOT NULL,
charval char(20) SPARSE NULL ,
varcharval varchar(20) SPARSE NULL,
intval int SPARSE NULL,
bigintval bigint SPARSE NULL
);
CREATE INDEX IX_sparse_intval_varcharval
ON dbo.sparse(intval,varcharval)
INCLUDE(bigintval,charval);
DML
I then inserted about 2540 NON-NULL values into both.
INSERT INTO dbo.nonsparse WITH(TABLOCK) (charval, varcharval,intval,bigintval)
SELECT 'Val1','Val2',20,19
FROM MASTER..spt_values;
INSERT INTO dbo.sparse WITH(TABLOCK) (charval, varcharval,intval,bigintval)
SELECT 'Val1','Val2',20,19
FROM MASTER..spt_values;
Afterwards, I inserted 1M NULL values into both tables
INSERT INTO dbo.nonsparse WITH(TABLOCK) (charval, varcharval,intval,bigintval)
SELECT TOP(1000000) NULL,NULL,NULL,NULL
FROM MASTER..spt_values spt1
CROSS APPLY MASTER..spt_values spt2;
INSERT INTO dbo.sparse WITH(TABLOCK) (charval, varcharval,intval,bigintval)
SELECT TOP(1000000) NULL,NULL,NULL,NULL
FROM MASTER..spt_values spt1
CROSS APPLY MASTER..spt_values spt2;
Queries
Nonsparse table execution
When running this query twice on the newly created nonsparse table:
SET STATISTICS IO, TIME ON;
SELECT * FROM dbo.nonsparse
WHERE 1= (SELECT 1) -- force non trivial plan
OPTION(RECOMPILE,MAXDOP 1);
The logical reads show 5257 pages
(1002540 rows affected)
Table 'nonsparse'. Scan count 1, logical reads 5257, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
And the cpu time is at 343 ms
SQL Server Execution Times:
CPU time = 343 ms, elapsed time = 3850 ms.
sparse table execution
Running the same query twice on the sparse table:
SELECT * FROM dbo.sparse
WHERE 1= (SELECT 1) -- force non trivial plan
OPTION(RECOMPILE,MAXDOP 1);
The reads are lower, 1763
(1002540 rows affected)
Table 'sparse'. Scan count 1, logical reads 1763, physical reads 3, read-ahead reads 1759, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
But the cpu time is higher, 547 ms.
SQL Server Execution Times:
CPU time = 547 ms, elapsed time = 2406 ms.
Sparse table execution plan
non sparse table execution plan
Questions
Original question
Since the NULL values are not stored directly in the sparse columns, could the increase in cpu time be due to returning the NULL values as a resultset? Or is it simply the behaviour as noted in the documentation?
Sparse columns reduce the space requirements for null values at the
cost of more overhead to retrieve nonnull values
Or is the overhead only related to reads & storage used?
Even when running ssms with the discard results after execution option, the cpu time of the sparse select was higher (407 ms) in comparison to the non sparse (219 ms).
EDIT
It might have been the overhead of the non null values, even if there are only 2540 present, but I am still not convinced.
This seems to be about the same performance, but the sparse factor was lost.
CREATE INDEX IX_Filtered
ON dbo.sparse(charval,varcharval,intval,bigintval)
WHERE charval IS NULL
AND varcharval IS NULL
AND intval IS NULL
AND bigintval IS NULL;
CREATE INDEX IX_Filtered
ON dbo.nonsparse(charval,varcharval,intval,bigintval)
WHERE charval IS NULL
AND varcharval IS NULL
AND intval IS NULL
AND bigintval IS NULL;
SET STATISTICS IO, TIME ON;
SELECT charval,varcharval,intval,bigintval FROM dbo.sparse WITH(INDEX(IX_Filtered))
WHERE charval IS NULL AND varcharval IS NULL
AND intval IS NULL
AND bigintval IS NULL
OPTION(RECOMPILE,MAXDOP 1);
SELECT charval,varcharval,intval,bigintval
FROM dbo.nonsparse WITH(INDEX(IX_Filtered))
WHERE charval IS NULL AND
varcharval IS NULL
AND intval IS NULL
AND bigintval IS NULL
OPTION(RECOMPILE,MAXDOP 1);
Seems to have about the same execution time:
SQL Server Execution Times:
CPU time = 297 ms, elapsed time = 292 ms.
SQL Server Execution Times:
CPU time = 281 ms, elapsed time = 319 ms.
But why are the logical reads the same amount now? Shouldn't the filtered index for the sparse column not store anything except the included ID field and some other non-data pages?
Table 'sparse'. Scan count 1, logical reads 5785,
Table 'nonsparse'. Scan count 1, logical reads 5785
And the size of both indices:
RowCounts Used_MB Unused_MB Total_MB
1000000 45.20 0.06 45.26
Why are these the same size? Was the sparse-ness lost?
Both query plans when using the filtered index
Extra Info
select @@version
Microsoft SQL Server 2017 (RTM-CU16) (KB4508218) -
14.0.3223.3 (X64) Jul 12 2019 17:43:08 Copyright (C) 2017 Microsoft Corporation Developer Edition (64-bit) on Windows Server
2012 R2 Datacenter 6.3 (Build 9600: ) (Hypervisor)
While running the queries and only selecting the ID field, the cpu time is comparable, with lower logical reads for the sparse table.
Size of the tables
SchemaName TableName RowCounts Used_MB Unused_MB Total_MB
dbo nonsparse 1002540 89.54 0.10 89.64
dbo sparse 1002540 27.95 0.20 28.14
When forcing either the clustered or nonclustered index, the cpu time difference remains.
sql-server sql-server-2017 sparse-column
Sparsing
When doing some tests on sparse columns, as you do, there was a performance setback that I would like to know the direct cause of.
DDL
I created two identical tables, one with 4 sparse columns and one with no sparse columns.
--Non Sparse columns table & NC index
CREATE TABLE dbo.nonsparse( ID INT IDENTITY(1,1) PRIMARY KEY NOT NULL,
charval char(20) NULL,
varcharval varchar(20) NULL,
intval int NULL,
bigintval bigint NULL
);
CREATE INDEX IX_Nonsparse_intval_varcharval
ON dbo.nonsparse(intval,varcharval)
INCLUDE(bigintval,charval);
-- sparse columns table & NC index
CREATE TABLE dbo.sparse( ID INT IDENTITY(1,1) PRIMARY KEY NOT NULL,
charval char(20) SPARSE NULL ,
varcharval varchar(20) SPARSE NULL,
intval int SPARSE NULL,
bigintval bigint SPARSE NULL
);
CREATE INDEX IX_sparse_intval_varcharval
ON dbo.sparse(intval,varcharval)
INCLUDE(bigintval,charval);
DML
I then inserted about 2540 NON-NULL values into both.
INSERT INTO dbo.nonsparse WITH(TABLOCK) (charval, varcharval,intval,bigintval)
SELECT 'Val1','Val2',20,19
FROM MASTER..spt_values;
INSERT INTO dbo.sparse WITH(TABLOCK) (charval, varcharval,intval,bigintval)
SELECT 'Val1','Val2',20,19
FROM MASTER..spt_values;
Afterwards, I inserted 1M NULL values into both tables
INSERT INTO dbo.nonsparse WITH(TABLOCK) (charval, varcharval,intval,bigintval)
SELECT TOP(1000000) NULL,NULL,NULL,NULL
FROM MASTER..spt_values spt1
CROSS APPLY MASTER..spt_values spt2;
INSERT INTO dbo.sparse WITH(TABLOCK) (charval, varcharval,intval,bigintval)
SELECT TOP(1000000) NULL,NULL,NULL,NULL
FROM MASTER..spt_values spt1
CROSS APPLY MASTER..spt_values spt2;
Queries
Nonsparse table execution
When running this query twice on the newly created nonsparse table:
SET STATISTICS IO, TIME ON;
SELECT * FROM dbo.nonsparse
WHERE 1= (SELECT 1) -- force non trivial plan
OPTION(RECOMPILE,MAXDOP 1);
The logical reads show 5257 pages
(1002540 rows affected)
Table 'nonsparse'. Scan count 1, logical reads 5257, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
And the cpu time is at 343 ms
SQL Server Execution Times:
CPU time = 343 ms, elapsed time = 3850 ms.
sparse table execution
Running the same query twice on the sparse table:
SELECT * FROM dbo.sparse
WHERE 1= (SELECT 1) -- force non trivial plan
OPTION(RECOMPILE,MAXDOP 1);
The reads are lower, 1763
(1002540 rows affected)
Table 'sparse'. Scan count 1, logical reads 1763, physical reads 3, read-ahead reads 1759, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
But the cpu time is higher, 547 ms.
SQL Server Execution Times:
CPU time = 547 ms, elapsed time = 2406 ms.
Sparse table execution plan
non sparse table execution plan
Questions
Original question
Since the NULL values are not stored directly in the sparse columns, could the increase in cpu time be due to returning the NULL values as a resultset? Or is it simply the behaviour as noted in the documentation?
Sparse columns reduce the space requirements for null values at the
cost of more overhead to retrieve nonnull values
Or is the overhead only related to reads & storage used?
Even when running ssms with the discard results after execution option, the cpu time of the sparse select was higher (407 ms) in comparison to the non sparse (219 ms).
EDIT
It might have been the overhead of the non null values, even if there are only 2540 present, but I am still not convinced.
This seems to be about the same performance, but the sparse factor was lost.
CREATE INDEX IX_Filtered
ON dbo.sparse(charval,varcharval,intval,bigintval)
WHERE charval IS NULL
AND varcharval IS NULL
AND intval IS NULL
AND bigintval IS NULL;
CREATE INDEX IX_Filtered
ON dbo.nonsparse(charval,varcharval,intval,bigintval)
WHERE charval IS NULL
AND varcharval IS NULL
AND intval IS NULL
AND bigintval IS NULL;
SET STATISTICS IO, TIME ON;
SELECT charval,varcharval,intval,bigintval FROM dbo.sparse WITH(INDEX(IX_Filtered))
WHERE charval IS NULL AND varcharval IS NULL
AND intval IS NULL
AND bigintval IS NULL
OPTION(RECOMPILE,MAXDOP 1);
SELECT charval,varcharval,intval,bigintval
FROM dbo.nonsparse WITH(INDEX(IX_Filtered))
WHERE charval IS NULL AND
varcharval IS NULL
AND intval IS NULL
AND bigintval IS NULL
OPTION(RECOMPILE,MAXDOP 1);
Seems to have about the same execution time:
SQL Server Execution Times:
CPU time = 297 ms, elapsed time = 292 ms.
SQL Server Execution Times:
CPU time = 281 ms, elapsed time = 319 ms.
But why are the logical reads the same amount now? Shouldn't the filtered index for the sparse column not store anything except the included ID field and some other non-data pages?
Table 'sparse'. Scan count 1, logical reads 5785,
Table 'nonsparse'. Scan count 1, logical reads 5785
And the size of both indices:
RowCounts Used_MB Unused_MB Total_MB
1000000 45.20 0.06 45.26
Why are these the same size? Was the sparse-ness lost?
Both query plans when using the filtered index
Extra Info
select @@version
Microsoft SQL Server 2017 (RTM-CU16) (KB4508218) -
14.0.3223.3 (X64) Jul 12 2019 17:43:08 Copyright (C) 2017 Microsoft Corporation Developer Edition (64-bit) on Windows Server
2012 R2 Datacenter 6.3 (Build 9600: ) (Hypervisor)
While running the queries and only selecting the ID field, the cpu time is comparable, with lower logical reads for the sparse table.
Size of the tables
SchemaName TableName RowCounts Used_MB Unused_MB Total_MB
dbo nonsparse 1002540 89.54 0.10 89.64
dbo sparse 1002540 27.95 0.20 28.14
When forcing either the clustered or nonclustered index, the cpu time difference remains.
sql-server sql-server-2017 sparse-column
sql-server sql-server-2017 sparse-column
edited 5 hours ago
Randi Vertongen
asked 9 hours ago
Randi VertongenRandi Vertongen
9,1393 gold badges12 silver badges33 bronze badges
9,1393 gold badges12 silver badges33 bronze badges
1
Could you get the plans for the post-edit query?
– George.Palacios
7 hours ago
1
@George.Palacios added them :)
– Randi Vertongen
6 hours ago
add a comment
|
1
Could you get the plans for the post-edit query?
– George.Palacios
7 hours ago
1
@George.Palacios added them :)
– Randi Vertongen
6 hours ago
1
1
Could you get the plans for the post-edit query?
– George.Palacios
7 hours ago
Could you get the plans for the post-edit query?
– George.Palacios
7 hours ago
1
1
@George.Palacios added them :)
– Randi Vertongen
6 hours ago
@George.Palacios added them :)
– Randi Vertongen
6 hours ago
add a comment
|
1 Answer
1
active
oldest
votes
Or is it simply the behaviour as noted in the documentation?
Seems so. The "overhead" mentioned in the documentation appears to be CPU overhead.
Profiling the two queries, the sparse query sampled 367 ms of CPU, while the non-sparse had 284 ms of CPU. That's a difference of 83 ms.

Where is most of that?
Both profiles look very similar until they get to sqlmin!IndexDataSetSession::GetNextRowValuesInternal. At that point, the sparse code goes down a path that runs sqlmin!IndexDataSetSession::GetDataLong, which calls some functions that look like they relate to the sparse column feature (HasSparseVector, StoreColumnValue), and add up to (42 + 11 =) 53 ms.

Why are these the same size? Was the sparse-ness lost?
Yeah, it appears that the sparse storage optimization does not carry over to nonclustered indexes when the sparse column is used as an index key. So nonclustered index key columns take up their full size regardless of sparseness, but included columns take up zero space if they are sparse and NULL.
Looking at DBCC PAGE output from a clustered index page with NULL-valued sparse columns, I can see that the record length is 11 (4 for the ID + 7 for the standard per-record overhead):
Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP Record Size = 11
For the filtered index, the record is always 40, which is the sum of the size of all the key columns (4 byte ID + 20 byte charval + 4 byte varcharval + 4 byte intval + 8 byte big intval = 40 bytes).
For some reason, DBCC PAGE does not include the 7 byte overhead in "Record Size" for index records:
Record Type = INDEX_RECORD Record Attributes = NULL_BITMAP Record Size = 40
The non-filtered index size is smaller (4 byte ID + 4 byte intval + 4 byte varcharval = 12 bytes) because two of the sparse columns are included columns, which again gets the sparseness optimization:
Record Type = INDEX_RECORD Record Attributes = NULL_BITMAP Record Size = 12
I guess this difference in behavior lines up with one of the limitations listed in the docs page:
A sparse column cannot be part of a clustered index or a unique primary key index
They are allowed to be keys in nonclustered indexes, but they are not stored, uh, sparsely.
1
Nice one! Thanks again!
– Randi Vertongen
4 hours ago
add a comment
|
Your Answer
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "182"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/4.0/"u003ecc by-sa 4.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fdba.stackexchange.com%2fquestions%2f249182%2fsparse-columns-cpu-time-filtered-indexes%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
Or is it simply the behaviour as noted in the documentation?
Seems so. The "overhead" mentioned in the documentation appears to be CPU overhead.
Profiling the two queries, the sparse query sampled 367 ms of CPU, while the non-sparse had 284 ms of CPU. That's a difference of 83 ms.

Where is most of that?
Both profiles look very similar until they get to sqlmin!IndexDataSetSession::GetNextRowValuesInternal. At that point, the sparse code goes down a path that runs sqlmin!IndexDataSetSession::GetDataLong, which calls some functions that look like they relate to the sparse column feature (HasSparseVector, StoreColumnValue), and add up to (42 + 11 =) 53 ms.

Why are these the same size? Was the sparse-ness lost?
Yeah, it appears that the sparse storage optimization does not carry over to nonclustered indexes when the sparse column is used as an index key. So nonclustered index key columns take up their full size regardless of sparseness, but included columns take up zero space if they are sparse and NULL.
Looking at DBCC PAGE output from a clustered index page with NULL-valued sparse columns, I can see that the record length is 11 (4 for the ID + 7 for the standard per-record overhead):
Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP Record Size = 11
For the filtered index, the record is always 40, which is the sum of the size of all the key columns (4 byte ID + 20 byte charval + 4 byte varcharval + 4 byte intval + 8 byte big intval = 40 bytes).
For some reason, DBCC PAGE does not include the 7 byte overhead in "Record Size" for index records:
Record Type = INDEX_RECORD Record Attributes = NULL_BITMAP Record Size = 40
The non-filtered index size is smaller (4 byte ID + 4 byte intval + 4 byte varcharval = 12 bytes) because two of the sparse columns are included columns, which again gets the sparseness optimization:
Record Type = INDEX_RECORD Record Attributes = NULL_BITMAP Record Size = 12
I guess this difference in behavior lines up with one of the limitations listed in the docs page:
A sparse column cannot be part of a clustered index or a unique primary key index
They are allowed to be keys in nonclustered indexes, but they are not stored, uh, sparsely.
1
Nice one! Thanks again!
– Randi Vertongen
4 hours ago
add a comment
|
Or is it simply the behaviour as noted in the documentation?
Seems so. The "overhead" mentioned in the documentation appears to be CPU overhead.
Profiling the two queries, the sparse query sampled 367 ms of CPU, while the non-sparse had 284 ms of CPU. That's a difference of 83 ms.

Where is most of that?
Both profiles look very similar until they get to sqlmin!IndexDataSetSession::GetNextRowValuesInternal. At that point, the sparse code goes down a path that runs sqlmin!IndexDataSetSession::GetDataLong, which calls some functions that look like they relate to the sparse column feature (HasSparseVector, StoreColumnValue), and add up to (42 + 11 =) 53 ms.

Why are these the same size? Was the sparse-ness lost?
Yeah, it appears that the sparse storage optimization does not carry over to nonclustered indexes when the sparse column is used as an index key. So nonclustered index key columns take up their full size regardless of sparseness, but included columns take up zero space if they are sparse and NULL.
Looking at DBCC PAGE output from a clustered index page with NULL-valued sparse columns, I can see that the record length is 11 (4 for the ID + 7 for the standard per-record overhead):
Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP Record Size = 11
For the filtered index, the record is always 40, which is the sum of the size of all the key columns (4 byte ID + 20 byte charval + 4 byte varcharval + 4 byte intval + 8 byte big intval = 40 bytes).
For some reason, DBCC PAGE does not include the 7 byte overhead in "Record Size" for index records:
Record Type = INDEX_RECORD Record Attributes = NULL_BITMAP Record Size = 40
The non-filtered index size is smaller (4 byte ID + 4 byte intval + 4 byte varcharval = 12 bytes) because two of the sparse columns are included columns, which again gets the sparseness optimization:
Record Type = INDEX_RECORD Record Attributes = NULL_BITMAP Record Size = 12
I guess this difference in behavior lines up with one of the limitations listed in the docs page:
A sparse column cannot be part of a clustered index or a unique primary key index
They are allowed to be keys in nonclustered indexes, but they are not stored, uh, sparsely.
1
Nice one! Thanks again!
– Randi Vertongen
4 hours ago
add a comment
|
Or is it simply the behaviour as noted in the documentation?
Seems so. The "overhead" mentioned in the documentation appears to be CPU overhead.
Profiling the two queries, the sparse query sampled 367 ms of CPU, while the non-sparse had 284 ms of CPU. That's a difference of 83 ms.

Where is most of that?
Both profiles look very similar until they get to sqlmin!IndexDataSetSession::GetNextRowValuesInternal. At that point, the sparse code goes down a path that runs sqlmin!IndexDataSetSession::GetDataLong, which calls some functions that look like they relate to the sparse column feature (HasSparseVector, StoreColumnValue), and add up to (42 + 11 =) 53 ms.

Why are these the same size? Was the sparse-ness lost?
Yeah, it appears that the sparse storage optimization does not carry over to nonclustered indexes when the sparse column is used as an index key. So nonclustered index key columns take up their full size regardless of sparseness, but included columns take up zero space if they are sparse and NULL.
Looking at DBCC PAGE output from a clustered index page with NULL-valued sparse columns, I can see that the record length is 11 (4 for the ID + 7 for the standard per-record overhead):
Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP Record Size = 11
For the filtered index, the record is always 40, which is the sum of the size of all the key columns (4 byte ID + 20 byte charval + 4 byte varcharval + 4 byte intval + 8 byte big intval = 40 bytes).
For some reason, DBCC PAGE does not include the 7 byte overhead in "Record Size" for index records:
Record Type = INDEX_RECORD Record Attributes = NULL_BITMAP Record Size = 40
The non-filtered index size is smaller (4 byte ID + 4 byte intval + 4 byte varcharval = 12 bytes) because two of the sparse columns are included columns, which again gets the sparseness optimization:
Record Type = INDEX_RECORD Record Attributes = NULL_BITMAP Record Size = 12
I guess this difference in behavior lines up with one of the limitations listed in the docs page:
A sparse column cannot be part of a clustered index or a unique primary key index
They are allowed to be keys in nonclustered indexes, but they are not stored, uh, sparsely.
Or is it simply the behaviour as noted in the documentation?
Seems so. The "overhead" mentioned in the documentation appears to be CPU overhead.
Profiling the two queries, the sparse query sampled 367 ms of CPU, while the non-sparse had 284 ms of CPU. That's a difference of 83 ms.

Where is most of that?
Both profiles look very similar until they get to sqlmin!IndexDataSetSession::GetNextRowValuesInternal. At that point, the sparse code goes down a path that runs sqlmin!IndexDataSetSession::GetDataLong, which calls some functions that look like they relate to the sparse column feature (HasSparseVector, StoreColumnValue), and add up to (42 + 11 =) 53 ms.

Why are these the same size? Was the sparse-ness lost?
Yeah, it appears that the sparse storage optimization does not carry over to nonclustered indexes when the sparse column is used as an index key. So nonclustered index key columns take up their full size regardless of sparseness, but included columns take up zero space if they are sparse and NULL.
Looking at DBCC PAGE output from a clustered index page with NULL-valued sparse columns, I can see that the record length is 11 (4 for the ID + 7 for the standard per-record overhead):
Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP Record Size = 11
For the filtered index, the record is always 40, which is the sum of the size of all the key columns (4 byte ID + 20 byte charval + 4 byte varcharval + 4 byte intval + 8 byte big intval = 40 bytes).
For some reason, DBCC PAGE does not include the 7 byte overhead in "Record Size" for index records:
Record Type = INDEX_RECORD Record Attributes = NULL_BITMAP Record Size = 40
The non-filtered index size is smaller (4 byte ID + 4 byte intval + 4 byte varcharval = 12 bytes) because two of the sparse columns are included columns, which again gets the sparseness optimization:
Record Type = INDEX_RECORD Record Attributes = NULL_BITMAP Record Size = 12
I guess this difference in behavior lines up with one of the limitations listed in the docs page:
A sparse column cannot be part of a clustered index or a unique primary key index
They are allowed to be keys in nonclustered indexes, but they are not stored, uh, sparsely.
edited 4 hours ago
answered 5 hours ago
Josh DarnellJosh Darnell
13.3k4 gold badges31 silver badges62 bronze badges
13.3k4 gold badges31 silver badges62 bronze badges
1
Nice one! Thanks again!
– Randi Vertongen
4 hours ago
add a comment
|
1
Nice one! Thanks again!
– Randi Vertongen
4 hours ago
1
1
Nice one! Thanks again!
– Randi Vertongen
4 hours ago
Nice one! Thanks again!
– Randi Vertongen
4 hours ago
add a comment
|
Thanks for contributing an answer to Database Administrators Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fdba.stackexchange.com%2fquestions%2f249182%2fsparse-columns-cpu-time-filtered-indexes%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
1
Could you get the plans for the post-edit query?
– George.Palacios
7 hours ago
1
@George.Palacios added them :)
– Randi Vertongen
6 hours ago