问题
I'm preparing a stored procedure in SQL Server. Basically, I have two tables here, table A & B, and the two tables are joined on Col4
. This stored procedure will filter table A based on the attributes in table B, say Col41
and Col42
in the where
clause (both Col41
and Col42
are of float
datatype).
Table A:
Col1 Col2 Col3 Col4
Row1 ** ** ** **
Row2 ** ** ** **
Table B:
Col5 Col4 Col41 Col42
Row1 ** ** ** **
Row2 ** ** ** **
There are lots of records in the two tables, so I plan to use non-clustered index to help the query (both tables have their first column set as primary key).
When I created this non-clustered index as shown below, and then dropped it after use inside the stored procedure (temporary index), the performance is pretty good.
CREATE NONCLUSTERED INDEX IX_1 ON tableB (Col41, Col42)
DROP INDEX IF EXISTS IX_1 ON tableB
However, if I directly added the same non-clustered index to tableB (permanent index), either right-click index folder of tableB or run the following code, the performance is much worse. it seems that even though the non-clustered index has been created, it has not been used.
CREATE NONCLUSTERED INDEX IX_1 ON tableB (Col41, Col42)
Cannot figure out why the running results turned out to be like this. Can anyone give any idea on how to solve it? Thanks.
回答1:
For your case, you need two indexes -
tableB(Col41, Col42)
tableA(Col4)
If you still see problem, as AlwaysLearning mentioned, check the execution plan, it can tell you the details how the data will be accessed.
回答2:
As @Mitch Wheat mentioned, adding an index does not make sure that it will always be used. One more thing, don't create and drop indexes in a stored procedure, unless you are creating on a temporary table. creating indexes is a costly operation. Better to do it, separately outside the development code.
It is depending on the query and selectivity of the index for the specific query. There is something called as "Tipping Point" which will check, whether using index or directly reading from the table would be good for a specific query. Read more on Tipping Point by Kimberly Tripp
Regarding your query, when you are telling that col41 and col42 are always going to be used for filter and going to use Col4 to join with TableA, I would suggest you to covering index like below, to make the index seekable. If the combination is unique, create unique index.
CREATE NONCLUSTERED INDEX Idx_TableB_Col41_Col42_Col4 ON TableB(Col41,Col42,Col4)
But, here you have to see the selectivity of the columns to see the order of the columns : Col41 and Col42. The more selective column will come in the left side followed by less selective column in the right side.
For finding selectivity of the column, see the column statistics. there will be second resultset, which tells the density of the column. the more lesser it is, the more selective the column is.
EXEC DBCC SHOW_STATISTICS(TableB, ColumnStatisticsName)
Also, create index on TableA for Col4. If the column is unique, create unique index.
CREATE NONCLUSTERED INDEX Idx_TableA_col4 ON TableA(Col4)
CAVEAT: The above also does not guarantee that index will always be used. Without seeing execution plan and actual query, it is very difficult to tell. The above guidelines also specifically for one query. I would suggest you to see, if you can consolidate the above index with existing queries to other indexes to reduce the index footprint on the table.
来源:https://stackoverflow.com/questions/62746985/how-to-correctly-create-non-clustered-index-to-help-stored-procedure