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;








7















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.










share|improve this question





















  • 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

















7















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.










share|improve this question





















  • 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













7












7








7


1






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.










share|improve this question
















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






share|improve this question















share|improve this question













share|improve this question




share|improve this question








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












  • 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










1 Answer
1






active

oldest

votes


















3

















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.



screenshot from Perfview showing total CPU for thread that ran the query



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.



screenshot of CPU difference for sparse column




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.






share|improve this answer






















  • 1





    Nice one! Thanks again!

    – Randi Vertongen
    4 hours ago













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
);



);














draft saved

draft discarded
















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









3

















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.



screenshot from Perfview showing total CPU for thread that ran the query



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.



screenshot of CPU difference for sparse column




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.






share|improve this answer






















  • 1





    Nice one! Thanks again!

    – Randi Vertongen
    4 hours ago















3

















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.



screenshot from Perfview showing total CPU for thread that ran the query



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.



screenshot of CPU difference for sparse column




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.






share|improve this answer






















  • 1





    Nice one! Thanks again!

    – Randi Vertongen
    4 hours ago













3














3










3










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.



screenshot from Perfview showing total CPU for thread that ran the query



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.



screenshot of CPU difference for sparse column




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.






share|improve this answer
















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.



screenshot from Perfview showing total CPU for thread that ran the query



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.



screenshot of CPU difference for sparse column




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.







share|improve this answer














share|improve this answer



share|improve this answer








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












  • 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


















draft saved

draft discarded















































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.




draft saved


draft discarded














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





















































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







Popular posts from this blog

Canceling a color specificationRandomly assigning color to Graphics3D objects?Default color for Filling in Mathematica 9Coloring specific elements of sets with a prime modified order in an array plotHow to pick a color differing significantly from the colors already in a given color list?Detection of the text colorColor numbers based on their valueCan color schemes for use with ColorData include opacity specification?My dynamic color schemes

Invision Community Contents History See also References External links Navigation menuProprietaryinvisioncommunity.comIPS Community ForumsIPS Community Forumsthis blog entry"License Changes, IP.Board 3.4, and the Future""Interview -- Matt Mecham of Ibforums""CEO Invision Power Board, Matt Mecham Is a Liar, Thief!"IPB License Explanation 1.3, 1.3.1, 2.0, and 2.1ArchivedSecurity Fixes, Updates And Enhancements For IPB 1.3.1Archived"New Demo Accounts - Invision Power Services"the original"New Default Skin"the original"Invision Power Board 3.0.0 and Applications Released"the original"Archived copy"the original"Perpetual licenses being done away with""Release Notes - Invision Power Services""Introducing: IPS Community Suite 4!"Invision Community Release Notes

François Viète Contents Biography Work and thought Bibliography See also Notes Further reading External links Navigation menup. 21Google Bookspp. 75–77Google BooksDe thou (from University of Saint Andrews)ArchivedGoogle BooksGoogle BooksGoogle BooksGoogle booksGoogle Bookscc-parthenay.frL'histoire universelle (fr)Universal History (en)ArchivedAdsabs.harvard.eduPagesperso-orange.frArchive.orgChikara Sasaki. Descartes' mathematical thought p.259Google BooksGoogle BooksGoogle Bookspp. 152 and onwardGoogle BooksGoogle BooksScribd.comGoogle Books1257-7979Google BooksGoogle BooksGoogle BooksGoogle BooksGoogle BooksGoogle BooksGallica.bnf.frGoogle BooksGoogle Books"François Viète"Francois Viète: Father of Modern Algebraic NotationThe Lawyer and the GamblerAbout TarporleySite de Jean-Paul GuichardL'algèbre nouvelle"About the Harmonicon"cb120511976(data)1188044800000 0001 0913 5903n82164680ola2013766880073431702w6vt1sb70287374827140948071409480