SQL Server查询性能优化之创建合理的索引(下)

发表于:2012-7-06 09:44

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:懒惰的肥兔    来源:51Testing软件测试网采编

  3、条件1、2、3都确定不了时那就用经常被排序的列吧,我们的很多操作都需要先进行排序才可以进行进一步查询,比如group by,like等操作都是要先进行排序操作才可以完成下一步查询。

  补充一点:可以把经常被返回的列放到索引的include里面去,在不增加索引键大小的情况下尽可能覆盖尽可能多的列,这样当遇到某些查询,没有用到组合索引的先导列,但又感觉不值得为其建立索引时,若此查询用到得字段被组合索引实现了索引覆盖,可以进行非聚集索引扫描完成查询(当非聚集索引实现了索引覆盖时,进行非聚集索引扫描有着比聚集索引扫描更好的效率)。

  下面我们用一些示例来简单说明下

CREATE TABLE Orders
(
    ID INT IDENTITY PRIMARY KEY,--自增列ID做主键,这样创建默认就成了聚集索引
    OrderCode BIGINT NOT NULL,--订单号
    Price DECIMAL(18,2) NOT NULL,--订单金额
    UserID INT NOT NULL,--用户ID
    [Status] INT NOT NULL,--订单状态
    PostTime DATETIME NOT NULL,--下单时间
    Moblie CHAR(11) NOT NULL,--用户手机号
    [Address] NVARCHAR(200) NOT NULL--收获地址
)

  创建一个订单表,按照我们的习惯有用没用来个自增列ID做主键,随随便便也就建立了聚集索引,现在我们来看一下,对于订单表来我们一般都会一组规则生成订单号,而不是简单的使用自增ID,因此我们创建了OrderCode用作订单号,当然了订单号必须是唯一的,因此需要创建唯一约束,过了些日子有其它人用到订单表或你自己用这个表,难免就会有些疑惑,OrderCode需要唯一,在这个表里到底是不是唯一的呢,于是乎你首先查看OrderCode上面是否建立了唯一约束,然后知道OrderCode就是唯一的,这也没啥,但是来个人都要查一遍,过段时间忘了还得再确认一次,很是麻烦,再看看我们那个主键ID,他神马都没干,就在那里呆着,现在拿掉它,于是表变为

CREATE TABLE Orders
(
    OrderCode BIGINT NOT NULL PRIMARY KEY,--订单号
    Price DECIMAL(18,2) NOT NULL,--订单金额
    UserID INT NOT NULL,--用户ID
    [Status] INT NOT NULL,--订单状态
    PostTime DATETIME NOT NULL,--下单时间
    Moblie CHAR(11) NOT NULL,--用户手机号
    [Address] NVARCHAR(200) NOT NULL--收获地址
)

  现在不管谁、什么时候看到这个表基本上不会怀疑OrderCode是否唯一了,一个不起眼的小改进,带来了很大的便利,所以主键自解释很有必要的

  我们看一下以下几个可能经常用到的查询

--查询1:指定用户特定时间内的所有订单
SELECT * FROM dbo.Orders WHERE UserID=1 AND PostTime BETWEEN '2012-6-1' AND '2012-6-30'
--查询2:指定用户的单个订单
SELECT * FROM dbo.Orders WHERE UserID=1 AND OrderCode=22222222222
--查询3:指定用户特定时间内特定状态的订单
SELECT * FROM dbo.Orders WHERE UserID=1 AND Status=1 AND PostTime BETWEEN '2012-6-1' AND '2012-6-30'
--查询4:指定时间内所有的订单
SELECT * FROM dbo.Orders WHERE PostTime BETWEEN '2012-6-1' AND '2012-6-30' AND Status=1

  为了最优查询速度有可能会创建以下索引

索引1:
CREATE INDEX IX_UserIDPostTime ON dbo.Orders(UserID,PostTime)
索引2:
CREATE INDEX IX_UserIDOrderCode ON dbo.Orders(UserID,OrderCode)
索引3:
CREATE INDEX IX_UserIDStatusPostTime ON dbo.Orders(UserID, Status,PostTime)
索引4:
CREATE INDEX IX_PostTimeStatus ON dbo.Orders(PostTime,Status)

  最悲观的情况下上面4个索引可能同时存在,为每一个查询建立对应的索引固然可行,但代价未免太大,别忘了索引不是大白菜。因此我们应尽可能的用少的索引覆盖多的查询。来看下上面的索引,如果只创建了索引1,那么只有查询1、3能从索引1受益,查询4没用用到索引1的先导列故不会用到索引1,查询2由于聚集索引存在根本不需要额外的非聚集索引。而索引2由于聚集索引的存在更是完全没必要存在,因此首先干掉索引2。再看索引3,索引3可以覆盖查询1和查询3、查询4,但由于索引列顺序问题使其在应对查询4时基本无效,对查询1虽然有效但效果不尽如人意,我们对索引3做下简单调整,把PostTime列和Status列顺序互换,修改后索引3对原查询3基本没影响,而且对查询1的效率提升也达到最大化

修改后的索引3:
CREATE INDEX IX_UserIDPostTimeStatus ON dbo.Orders(UserID,PostTime,Status)

  现在索引3可以很好的完成查询1和查询3,因此索引1现在可以删除掉,现在只剩索引3和索引4了,我们可以看到修改后的索引3由于先导列问题依然无法用于查询4,为了使索引3用于查询4我们再次修改索引3,把PostTime放到索引的第一列,其它列保持顺序不变

再次修改后的索引3:
CREATE INDEX IX_PostTimeUserIDStatus ON dbo.Orders(PostTime,UserID,Status)

32/3<123>
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号