Problem Description / Requirement
A new customer contacts you. Their developers have created a dashboard that contains five KPI components that visualize the importance of orders according to a given status. The status of an order can only have 3 states!
- high
- normal
- low
To take future states into account, two more KPI components were added to the dashboard. The customer was told that querying additional values would not affect performance because there was a CHECK constraint on the [o_orderpriority] attribute that only coveres values 1 – 3. SQL Server would ignore other values.
When examining the performance, it was found that for ALL parameter values (1 – 5) a FULL SCAN was always performed on the index.
– Why does this happen?
– Explain to the customer how this could be prevented.
The basis for the analysis are the following SQL statements, which you have been able to evaluate:
/* Dashboard KPI Query #1 */ SELECT COUNT_BIG(*) FROM dbo.orders WHERE o_orderpriority = 1; GO /* Dashboard KPI Query #2 */ SELECT COUNT_BIG(*) FROM dbo.orders WHERE o_orderpriority = 2; GO /* Dashboard KPI Query #3 */ SELECT COUNT_BIG(*) FROM dbo.orders WHERE o_orderpriority = 5; GO
Challenge
All queries generate the same execution plan and your customer wants to know why this happens. He also wants to receive a solution from you on how to avoid this.
Condition
- No new indexes are allowed to add to the table due to concerns about write performance.
- Metadata of the table must not be changed.
Category
Constraints / Consulting
Level 300
Technical Information
On table is created in the challenge database that are relevant for this task:
Table Name | Number of Rows | Storage Space |
dbo.orders | 1.000.000 | 14,25 MB |
The table has a clustered primary key and a CHECK Constraint on o_orderpriority.
How to start the challenge
When you are connected to the SQL Server instance…
EXECUTE sqlchallenge.dbo.create_challenge 'list'; |
will give you a list of open challenges |
EXECUTE sqlchallenge.dbo.create_challenge 'name_of_challenge'; |
will create the challenge for you |
After the creation just get into the database for your challenge
customer expectations
The customer wants to know why queries against predicates which are not in the range of the CHECK Constraints will cause a SCAN of the table. Furthermore he wants to have a solution to fix it without adding new indexes to the table.
Connect to Microsoft SQL Server
SQL Server: | sqlchallenges.germanywestcentral.cloudapp.azure.com |
Port: | 1433 |
Version: |
16.0.4165.4
|
Edition: | Developer Edition |
Authentication Type: | SQL Server Authentication |
Login Name: | sqlchallenge |
Password: | sqlchallenge#2025! |
Encryption | Mandatory |
Trust Server Certificate: | True |
Improve Your Skills as Query Tuner for SQL Server
During this workshop, you’ll gain the expertise and confidence needed to diagnose and fine-tune slow-performing queries, resulting in improved database responsiveness and end-user satisfaction. Whether you’re an experienced database administrator, developer, or data analyst, this workshop will equip you with the knowledge and practical skills to tackle the most challenging query optimization tasks.
Improve Your Skills as a Database Developer for SQL Server
In this workshop, we will cover a wide range of topics and techniques that are essential for modern database developers working with SQL Server. From fundamental concepts to advanced best practices, you’ll gain the knowledge and practical experience needed to excel in your role.
If we can use an indexed view, it will be a good solution.
create VIEW v_order_priority_counts WITH SCHEMABINDING AS
SELECT o_orderkey, o_orderpriority , licz = count_big(*)
FROM dbo.orders
WHERE o_orderpriority in (1,2,3)
group by o_orderkey, o_orderpriority
CREATE UNIQUE CLUSTERED INDEX xdsdsd
ON v_order_priority_counts(o_orderkey);
CREATE NONCLUSTERED INDEX [test]
ON [dbo].[v_order_priority_counts] ([order_count])
And…
SELECT COUNT_BIG(*) FROM v_order_priority_counts WITH(NOEXPAND) WHERE o_orderpriority = 3;
0.000s
And for table we have to use something like this (Erik Darling lessons) to avoid trivial plan. Moreover table can use index from indexed view
SELECT COUNT_BIG(*) FROM dbo.orders WHERE o_orderpriority = 2 AND 1 = (SELECT 1);
0.000s
Of course:
CREATE NONCLUSTERED INDEX [test]
ON [dbo].[v_order_priority_counts] ( [o_orderpriority])
CREATE PROCEDURE #List_orders_5 @order_id BIGINT
AS
BEGIN
IF @order_id = 1
BEGIN
SELECT COUNT_BIG(*) FROM dbo.orders WHERE o_orderpriority = 1;
END
ELSE IF @order_id >= 3
BEGIN
SELECT COUNT_BIG(*) FROM dbo.orders WHERE o_orderpriority = @order_id OPTION (RECOMPILE);
END
END;
My initial guess was that CHECK constraint had WITH NOCHECK option 🙂
SQL does not optimize such simple queries, query optimization is TRIVIAL and information in check constraint it not used.
In order to force full optimization, one has to complicate query, for example:
/* Dashboard KPI Query #3 */
SELECT COUNT_BIG(*) FROM dbo.orders where o_orderpriority = (select 5)
SELECT COUNT_BIG(*) FROM dbo.orders where 1=1 ADN o_orderpriority = 5
SELECT COUNT_BIG(*) FROM dbo.orders where o_orderpriority = 5 OPTION (MAXDOP 2)
Interesting observations:
#1
Full optimization is forced(?)/reported when query goes parallel.
This can be done by lowering cost threshold for parallelism.
In case like this, in my tests, index scan was still performed for all value despite full optimization.
#2
OPTION (RECOMPILE) works just fine despite query optimization reported as TRIVIAL:
SELECT COUNT_BIG(*) FROM dbo.orders where o_orderpriority = 5 OPTION (RECOMPILE)
Thanks for the challenge!
Hey Art,
super explanation. You hit the nail.
Due to a trivial plan and simple parameterization the plan is cached with a variable.
So the plan won’t change!
RECOMPILE is one option to deal with the problem.
Thumb up!
I know i’m late on this – But we can also explicitly tell it to avoid parameter sniffing in sql 2022
SELECT COUNT_BIG(*) FROM dbo.orders WHERE o_orderpriority = 1 OPTION (USE HINT(‘DISABLE_PARAMETER_SNIFFING’));
Hallo Peter,
thank you very much for your valued input. Due to the fact that we are running on SQL 2022 this is a valid option.
Just came across your SQL Challenges series — big thanks to you for kicking it off! Brings back memories to my time spend on BeyondRelational. I’ll definitely be following along with interest.