Identifying SQL Server 2014 New Cardinality Estimator issues and Service Pack 1 improvement


In a previous blog, I talked about SQL 2014’s new Cardinality Estimator (new CE) and trace flags 9481 and 2312 can be used to control which version of Cardinality Estimator is used.

In this blog, I will modify a real scenario customer hit to show you that how you can use these trace flags to spot issues related to new CE and how SQL 2014 SP1 can help.


Customer reported to us that a particular query ran slowly on SQL 2014.  After troubleshooting, we determined that new Cardinality Estimator grossly over-estimated.  We compared the plans using trace flag 2312 (new CE) and 9481 (old CE).

Here is a simplified repro without revealing customer’s table and data.  Customer’s query is more complex.  But the issue is the over-estimation of a particular join that produced a bad query plan overall.

The script below inserts 1 million rows in table t.  c1 is primary key.  c2 has duplicates( each number repeats 100 times).

create table t (c1 int primary key, c2 int)
set nocount on

begin tran
declare @i int
set @i = 0
while @i < 1000000

insert into t values (@i, @i % 10000)
set @i = @i +1

commit tran
create index ix on t(c2)
update statistics t


How many rows do you think the following query will return?  It will return 100 rows.  Note that I used querytraceon 2312 to force new CE on SQL 2014.  But if your database compatibility level is 120, you will get new CE without having to use the trace flag.  Again, the previous blog has instructions how to control new and old CE with trace flags and database compatibility level.

select t1.c2 from t t1 join t t2 on t1.c2 = t2.c2
where t1.c1 = 0 option (querytraceon 2312)


Do you think the query below will return more rows or less rows than the query above?  Note that I added an AND clause “t1.c1 <> t2.c1”.   This should have made it more restrictive and return no more rows than the previous query.  It actually returns 99 rows because there is one row filtered out by t1.c1 <> t2.c1.

select t1.c2 from t t1 join t t2 on t1.c2 = t2.c2 and t1.c1 <> t2.c1
where t1.c1 = 0 option (querytraceon 2312)

Let’s take a look at optimizer estimates.

The first query has very accurate estimate.


Take a look at the estimate below for secondary query.  The query has 99 rows returned but the estimate is 1,000,000 rows.    The difference is that I added one more AND predicate in the secondary query (t1.c1 <> t2.c1).  It should have cut down the estimate.  But it actually made it much larger.



Note that the same query has fairly low estimate by forcing old CE with trace flag 9481

select t1.c2 from t t1 join t t2 on t1.c2 = t2.c2 and t1.c1 <> t2.c1
where t1.c1 = 0 option (querytraceon 9481)



It is this behavior that made customer’s original query much slower. This is a bug in new Cardinality Estimator.

Customer called in for help tuning the query.  First we had them revert to old Cardinality Estimator by trace flag 9481 which made query fast.   We knew that we had quite a few fixes in this space and asked customer to apply SP1 on a test machine.  But the query was still slow.   So we went ahead and collected statistics clone and started to look at query in house.   We were able to reproduce the issue where new Cardinality Estimator had very high estimate but old Cardinality Estimator has low estimate even on SP1

We thought it’s a new bug after SP1.  But as we looked at the fix more closely, it required trace flag  4199 to be enabled.  In fact all optimizer fixes require 4199 to activate.    After enabling 4199, SP1 was able to estimate correctly.  Customer tested their original query on SP1 with trace flag 4199 and it ran fast.


You need to apply SP1 but you must also enable trace flag 4199 in order to activate the fix.

SQL Server 2014 Service Pack 1 made various fixes on new Cardinality Estimator (new CE).  The release notes also documents the fixes.

Jack Li |Senior Escalation Engineer | Microsoft SQL Server


twitter| pssdiag |Sql Nexus