0

I have a table whose definition looks like this:

CREATE TABLE Budget_Cost_Centers
(
    CostCenterIDX INT NOT NULL,
    Name VARCHAR(100) NOT NULL,
    ...
    CONSTRAINT PK_Budget_Cost_Centers 
        PRIMARY KEY CLUSTERED (CostCenterIDX ASC)
);

(some columns were left out because they are not relevant)

I now run the Database Tuning Advisor on a query against this table and it suggests:

CREATE NONCLUSTERED INDEX [_dta_index_Budget_Cost_Centers_5_1079674894__K1] 
ON [dbo].[Budget_Cost_Centers] ([COSTCENTERIDX] ASC)
   WITH (SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF) ON [PRIMARY]

Which is obviously the same as the existing primary key. Any idea why it would suggest this? Trying to make sure I'm not missing something about how SQL Server works as opposed to the advisor suggesting something ridiculous because that's just the limitations of automated tuning analysis.


More info: I broke down the query that was producing this recommendation to the bare essentials:

select
    d.PhaseIDX,
    sum(d.YTDActual) YTDActual
from 
    Budget_Cost_Centers cc
join 
    Budget_Cost_Centers mc on cc.MasterCostCenteridx = mc.CostCenterIDX
join 
    Budget_Detail d on cc.CostCenterIDX= d.CostCenterIDX
group by 
    d.PhaseIDX

The table self-references and the query author joined it to itself in order to filter the records to only those that contained said reference. Changing the query to the following equivalent results in the advisor no longer making the recommendation:

select
    d.PhaseIDX,
    sum(d.YTDActual) YTDActual
from 
    Budget_Cost_Centers cc
join 
    Budget_Detail d on cc.CostCenterIDX = d.CostCenterIDX
where 
    cc.MasterCostCenterIDX is not null
group by 
    d.PhaseIDX

Here is the execution plan with the original sql:

Execution Plan

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Blake
  • 200
  • 3
  • 10
  • This one is definitely a puzzler. The only thing I can think of is if the Primary Key has `SORT_IN_TEMPDB = ON` set. In that case, there could be cases where the other key is more efficient... but that still seems weird. – Laughing Vergil Nov 13 '17 at 18:38
  • Never seen anything like this. Did you see if your execution plan was using the existing index? Posting the actual EP might help others determine whats going on. – Joe C Nov 13 '17 at 18:50
  • I added more info, including execution plan, above. – Blake Nov 13 '17 at 20:00
  • This is very helpful and explains a lot. Quick Question: Are you comfortable that the modified query is equivalent to the original? The original seems to only return records where the MasterCostCenterIDX exists in CostCenterIDX. The modified one seems to return all Masters. That may be right for you as I don't understand how the table is being used. Good luck, John! – DanielG Nov 13 '17 at 20:37
  • You are correct and I realized after I posted the update that I should have said they are "functionally equivalent". However, there is a foreign key relationship that prevents the MasterCostCenterIDX column from being populated with anything that does not exist in the cost center table. Only because of that can it be considered the same. What's still not clear to me is why the tuning advisor would recommend what appears to be a duplicate index, even in this scenario. – Blake Nov 13 '17 at 21:07
  • Do you have enough rows to run a meaningful test with and without the index, and see if performance helps? I also wonder if adding an optimizer hint or Table hint to the existing query would change anything: WITH (INDEX(PK_Budget_Cost_Centers )). Interesting post, John!! – DanielG Nov 13 '17 at 22:02
  • The query hint forces the optimizer to use the index and advisor stops suggesting a new one. The line that changes in the execution plan is number 11. This: `Index Scan(OBJECT:([Helix].[dbo].[Budget_Cost_Centers].[IX_Budget_Cost_Centers_isoverhead] AS [mc]))` goes to this: `Clustered Index Scan(OBJECT:([Helix].[dbo].[Budget_Cost_Centers].[PK_Budget_Cost_Centers] AS [mc]))` Left to it's own (without the hint), it looks like the DB engine is just picking a random index to test for the existence of the record. – Blake Nov 14 '17 at 01:07

1 Answers1

0

The PK is clustered and the other is not clustered. There may be an advantage to having both.

Non-clustered index and clustered index on the same column

DanielG
  • 1,669
  • 1
  • 12
  • 26