具有以下结构:
Table Auction (Id_Auction (Pk), DateTime_Auction)
Table Auction_Item (Id_Auction_Item (Pk), Id_Auction (Fk), Id_Winning_Bid (Fk), Item_Description)
Table Bid (Id_Bid (Pk), Id_Auction_Item (Fk), Id_Bidder (Fk), Lowest_Value, Highest_Value)
Table Bidder (Id_Bidder (Pk), Name)
拍卖指数无关紧要。
Auction_Item的索引:
Clustered Index PK_Auction_Item (Id_Auction_Item)
NonClustered Index IX_Auction_Item_IdWinningBid (Id_Winning_Bid)
出价指数:
Clustered Index PK_Bid (Id_Bid)
NonClustered Index IX_Bid_IdBidder (Id_Bidder)
NonClustered Index IX_Bid_IdBid_IdBidder (Id_Bid, Id_Bidder) Unique Included (Id_Auction_Item, Lowest_Value, Highest_Value)
投标人的索引无关紧要。
我会请你稍微承认一下......这种结构只是让你认识到表/数据之间的关系,并不打算遵循最佳实践。实际的数据库确实更复杂(表“Bid”就像5400万行)。哦,是的,每个Auction_Item只有一个“每个投标人的出价”,其出价最高和最低。
所以,当我执行以下查询时:
Select
Auc.Id_Auction,
Itm.Id_Auction_Item,
Itm.Item_Description,
B.Id_Bid,
B.Lowest_Value,
B.Highest_Value
From
Auction Auc
Inner Join Auction_Item Itm on Itm.Id_Auction = Auc.Id_Auction
Inner Join Bid B on B.Id_Bid = Itm.Id_Winning_Bid
And B.Id_Bidder = 27
Where Auc.DateTime_Auction > '2014-01-01';
为什么Sql Server不使用“IX_Bid_IdBid_IdBidder”,并将此执行计划用于出价:
如果我禁用IX_Bid_IdBidder,并强制它使用“IX_Bid_IdBid_IdBidder”,一切都搞乱了:
我无法理解为什么MSSQL更喜欢使用2个索引,而不是只有一个完全覆盖查询的索引。我唯一的猜测是使用ClusteredIndex更快,但我不相信它比仅使用其他NonClustered Index的Unique Composite Key更快。
为什么呢?
更新 正如@Arvo提出的,我更改了“IX_Bid_IdBid_IdBidder”的键列顺序,使Id_Bidder成为第一,Id_Bid成为秒。然后,它成为首选索引。那么,为什么MSSQL使用选择性较低的“索引键”而不是最具选择性的键? Id_Bid在内部联接中明确相关...
旧更新: 我更新了查询,使其更具选择性。 另外,我更新了索引“IX_Bid_IdBid_IdBidder”,以包含Id_Auction_Item
道歉: 索引IX_Bid_IdAuctionItem_IdBidder实际上是IX_Bid_IdBid_IdBidder,它包含在INDEX UNIQUE KEY中的Id_Bid!
答案 0 :(得分:2)
SQL Server很少使用覆盖,正确排序的索引。只有病态案例才会出现,例如极低的页面填充度或非常不需要的额外列。
您的索引根本就没有涵盖。查看输出的列。您将发现一个尚未编入索引的文件。
该列为Id_Auction_Item
。
答案 1 :(得分:1)
好吧,我认为经过大量的研究(并且更多地了解了联接如何在幕后工作),我想出来了。
到现在为止,我只是将其作为一种理论发布,直到一些SQL大师说它错了并向我展示了光明,或者我确定我是对的。
重点是MSSQL正在选择整个查询的最快速度,而不仅仅是Bid表。所以分析器必须选择从Auction表或Bid表开始(因为我指定的条件.DateTime_Auction和Id_Bidder)。 在我(轻浮)的思想中,我认为最好的执行计划将从拍卖表开始:
获取与指定日期匹配的拍卖>>获取Auctions_Items匹配内部联接与拍卖>>获取与Auction_Item匹配内部联接的投标,并使Id_Bidder与指定的ID匹配
这会在每个" level" /嵌套循环中选择很多行,并且最后只使用指定的索引来排除90%的数据。
相反,MSSQL希望尽可能从最小的数据集开始。在这种情况下,只有指定投标人的投标,因为投标人可能根本没有参与的拍卖物品很多。这样做,每个嵌套循环的外表都缩小了与#34;我的计划"相比。
获取指定投标人的出价>>与Auction_Item>>的内部联接不包括与日期匹配的拍卖。
如果你注意最右边的嵌套循环,我认为是第一个嵌套循环,循环的外表是使用适当索引(IX_Bid_IdBidder)预先选择的投标人投标列表,而不是执行扫描聚集索引等...
为了使它更好,我列出了" IX_Bid_IdBid_IdBidder"中的列。进入" IX_Bid_IdBidder",并且MSSQL不需要在PK_Bid上执行密钥查找。
每次拍卖都有很多拍卖物品,但每个拍卖物品只有一个来自指定投标人的投标,因此第一个嵌套循环将选择我们需要的最少有效拍卖物品,这也将限制拍卖我们将考虑匹配日期。因此,由于我们从Bids开始,因此没有"列表"对Id_Bids进行限制,然后MSSQL不能使用索引" IX_Bid_IdBid_IdBidder"即使它涵盖了查询的所有领域。现在思考,似乎有点明显。
无论如何,感谢所有帮助过我的人!
我的研究:
http://sqlmag.com/database-performance-tuning/advanced-join-techniques(有点过时......)
https://technet.microsoft.com/en-us/library/ms191426%28v=sql.105%29.aspx
https://technet.microsoft.com/en-us/library/ms191318%28v=sql.105%29.aspx
http://blogs.msdn.com/b/craigfr/archive/2006/07/26/679319.aspx
http://blogs.msdn.com/b/craigfr/archive/2009/03/18/optimized-nested-loops-joins.aspx
答案 2 :(得分:0)
那里有很多人比我更了解SQL Server,但这听起来很像两个可能的问题之一:
首先,可能是SQL Server使用过时的统计信息来确定最有效的"以及由于统计信息错误,它选择了错误的索引。
第二种情况不太可能,但值得一提。您没有在文本中提到存储过程,但如果这是在存储过程中,则SQL可能正在使用缓存(并且非常错误)的执行计划 - 查找参数嗅探'有关此主题的更多解释。