Untrusted Check Constraints

Check constraints are useful for more than just verifying the data that goes into your tables. The query optimizer can use them to avoid looking for values that couldn’t possibly be there.

For example, imagine a listprice field with a constraint that keeps the field value over $100. If a query asks for the records with values under $100, the query optimizer can return the data without reading any records, just because of the constraint.

The problem is that the constraint can be disabled, and records can be inserted or updated in the meantime. SQL Server solves this problem by keeping a flag for each constraint, which states if the constraint is trusted or not.

The query optimizer only uses the constraint when it’s flagged as trusted. If it isn’t, the query optimizer won’t use it, and queries that look for values which don’t exist will have a higher cost.

Here’s an example you can try in your test environment, using NORTHWIND as a test database

CREATE TABLE  productTest    (  id INT IDENTITY(1, 1) ,

      productname VARCHAR(50) ,

      listprice DECIMAL       

— Constraint to check listprice field        CONSTRAINT chkprice CHECK ( listprice > 100 )    )

 GO

— import data from products table, but only valid records

INSERT  INTO productTest       

SELECT  productname ,

                unitprice

        FROM   products

        WHERE   unitprice > 100

Let’s check if the constraint is valid:

SELECT  name ,

        OBJECT_NAME(parent_object_id) “table” ,

        is_not_trusted

FROM    sys.check_constraints

valid con1

If you check the execution plans for the following queries, you’ll see they’re different, even though the results are the same.

SELECT  productname ,

        listprice

FROM    productTest

WHERE   listprice = 120.00OPTION  ( RECOMPILE ) 

chkcon2

 SELECT  productname ,

        listprice

FROM    productTest

WHERE   listprice = 50.00OPTION  ( RECOMPILE )

con3

The first one uses a clustered index scan to check if there are records with listprice $120. The second only needs a constant scan, because the constraint that there isn’t a value of $50 in the table.

The problem appears when you disable the constraint, perhaps because you want to import a lot of records that you’re already sure are valid:

ALTER TABLE productTest NOCHECK CONSTRAINT ALL

If you query the is_not_trusted flag again, you’ll notice that the constraint is already marked as not trusted, even before any records were inserted or updated.

con4

It will remain untrusted, even if you enable the constraint again, because any new records haven’t been checked:

ALTER TABLE productTest CHECK CONSTRAINT ALL

To solve this problem, you need to enable the constraint again with the CHECK option. This means the constraint will immediately check all the records in the table:

ALTER TABLE productTest WITH CHECK CHECK CONSTRAINT ALL

valid con1

Be sure to keep your constraints trusted, so the query optimizer can use them to avoid unnecessary reads.

Another Interesting stuff with help of Red-Gate

Advertisements

One thought on “Untrusted Check Constraints

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s