SQLServer - 聚集索引/非聚集索引

1、定义 + 区别

聚集索引将一张表中的多行数据按排好的顺序进行物理存储,一个表只能建一个;
非聚集索引是指向表中的数据并独立于表的一种物理存储结构,一个表可以建多个;
可见,建立非聚集索引需要冗余的表空间,好处就是可以建立多个,但速度要比聚集索引慢。

聚集索引和非聚集索引的根本区别是表记录的排列顺序和与索引的排列顺序是否一致,聚集索引表记录的排列顺序与索引的排列顺序一致,优点是查询速度快,因为一旦具有第一个索引值的纪录被找到,具有连续索引值的记录也一定物理的紧跟其后。聚集索引的缺点是对表进行修改速度较慢,这是为了保持表中的记录的物理顺序与索引的顺序一致,而把记录插入到数据页的相应位置,必须在数据页中进行数据重排,降低了执行速度。建议使用聚集索引的场合为:
  a.此列包含有限数目的不同值;
  b.查询的结果返回一个区间的值;
  c.查询的结果返回某值相同的大量结果集。
    1、您最频繁使用的、用以缩小查询范围的字段上;
    2、您最频繁使用的、需要排序的字段上。

非聚集索引指定了表中记录的逻辑顺序,但记录的物理顺序和索引的顺序不一致,聚集索引和非聚集索引都采用了B+树的结构,但非聚集索引的叶子层并不与实际的数据页相重叠,而采用叶子层包含一个指向表中的记录在数据页中的指针的方式。非聚集索引比聚集索引层次多,添加记录不会引起数据顺序的重组。建议使用非聚集索引的场合为:
  a.此列包含了大量数目不同的值;
  b.查询的结束返回的是少量的结果集;
  c.order   by   子句中使用了该列。


--不用索引查询  
SELECT   *   FROM   IndexTestTable   WHIT(INDEX(0))  
WHERE   Status= 'B '  

--创建聚集索引  
CREATE   CLUSTERED   INDEX   icIndexTestTable  
ON   IndexTestTable(Status)  
GO  

--使用索引查询  
SELECT   *   FROM   IndexTestTable   WITH(INDEX(icIndexTestTable))  
WHERE   Status= 'B '
   

扫描二维码关注公众号,回复: 1420144 查看本文章

来源(CSDN问答):http://topic.csdn.net/t/20030114/11/1356598.html

2、聚集索引:
  该索引中键值的逻辑顺序决定了表中相应行的物理顺序。

如果用 新华字典 作例子来一个例子的话。
  [拼音]就可以看作是聚集索引
    例如 吖、阿、啊 在字典的最前面。
    左、作、坐 在字典的最后面。
    拼音[逻辑顺序]很接近,在字典中页数的位置[物理顺序]也很接近。

适用场合:
  含有大量非重复值的列
  使用BETWEEN,>,>=,<或<=返回一个范围值的列
  被连续访问的列
  返回大型结果集的查询
  经常被使用连接或GROUP BY子句的查询访问的列



非聚集索引:
  非聚集索引与聚集索引一样有 B 树结构,但是有两个重大差别:
  数据行不按非聚集索引键的顺序排序和存储。
  非聚集索引的叶层不包含数据页。
  相反,叶节点包含索引行。每个索引行包含非聚集键值以及一个或多个行定位器,
  这些行定位器指向有该键值的数据行(如果索引不唯一,则可能是多行)。

如果用 新华字典 作例子来一个例子的话。
  [笔画]就可以看作是非聚集索引
    例如 化 仇 仃 仅 仂 这几个字,都是 单人旁,笔画数相同的。
    笔画[逻辑顺序]很接近,在字典中页数的位置[物理顺序]则在不同的位置上。

适用场合:
  含有大量非重复值的列
  非连续访问的列
  返回小型结果集的查询



-- 由于 SQL SERVER 默认是在主键上建立聚集索引的。
-- 一个表,又只允许有一个 聚集索引。
-- 这里创建主键的时候,要指定这个主键,使用非聚集索引
CREATE TABLE TestDoc(
  id             INT identity(1, 1),
  createDate      DATETIME,
  owner            VARCHAR(10),
  docInfo        TEXT,
  PRIMARY KEY NONCLUSTERED (id)
);
go

 

 

 

-- 首先插入 36500*5 = 182500 条数据.
DECLARE
  @i AS INT,
  @myDate AS DATETIME;
BEGIN
  SET @i = 0;
  SET @myDate = CONVERT(DATETIME, '1949.10.01', 102);
  WHILE @i < 36500
  BEGIN
    SET @i = @i + 1;
    INSERT INTO TestDoc
        VALUES ( DATEADD(dd, @i, @myDate), '张三', NULL);
    INSERT INTO TestDoc
        VALUES ( DATEADD(dd, @i, @myDate), '李四', NULL);
    INSERT INTO TestDoc
        VALUES ( DATEADD(dd, @i, @myDate), '王五', NULL);
    INSERT INTO TestDoc
        VALUES ( DATEADD(dd, @i, @myDate), '赵六', NULL);
    INSERT INTO TestDoc
        VALUES ( DATEADD(dd, @i, @myDate), 'Admin', NULL);
  END;
END
go


-- 复制自己一次 = 182500 * 2 = 365000
INSERT INTO TestDoc
  SELECT createDate, owner + '1', docInfo FROM TestDoc;
go

-- 再自己复制自己一次 = 365000 *2 = 730000
INSERT INTO TestDoc
  SELECT createDate, owner + '2', docInfo FROM TestDoc;
go

-- 再自己复制自己一次 = 730000 *2 = 1460000
INSERT INTO TestDoc
  SELECT createDate, owner + '3', docInfo FROM TestDoc;
go

-- 再自己复制自己一次 = 1460000 *2 = 2920000
INSERT INTO TestDoc
  SELECT createDate, owner + '4', docInfo FROM TestDoc;
go

 

 

-- 用于测试执行性能的存储过程.
-- 该存储过程,只在很大的数据量中,查询很少量的数据. [1/36500]
CREATE PROCEDURE TestQuery1
AS
BEGIN
  SET NOCOUNT ON;

  IF EXISTS(SELECT * FROM sys.Tables WHERE name='#temp1')
  DROP TABLE #temp1;

  IF EXISTS(SELECT * FROM sys.Tables WHERE name='#temp2')
  DROP TABLE #temp2;


  -- 定义一个开始之间.
  DECLARE @startDate AS DATETIME;

  -- 首先只查询一个字段的
  -- 设置 开始执行时间.
  SET @startDate = GETDATE();
  -- 执行查询.
  SELECT
    * INTO #temp1
  FROM
    TestDoc
  WHERE
    createDate = CONVERT(DATETIME, '2008.01.01', 102);
  -- 输出查询经过的时间.
  PRINT DATEDIFF(MS, @startDate, GETDATE());


  -- 然后查询多个字段的
  -- 设置 开始执行时间.
  SET @startDate = GETDATE();
  -- 执行查询.
  SELECT
    * INTO #temp2
  FROM
    TestDoc
  WHERE
    createDate = CONVERT(DATETIME, '2008.01.01', 102)
    AND owner LIKE '张三%';
  -- 输出查询经过的时间.
  PRINT DATEDIFF(MS, @startDate, GETDATE());

END
go




-- 用于测试执行性能的存储过程.
-- 该存储过程,在很大的数据量中,查询比较大量的数据. [1/100]
CREATE PROCEDURE TestQuery2
AS
BEGIN
  SET NOCOUNT ON;

  IF EXISTS(SELECT * FROM sys.Tables WHERE name='#temp1')
  DROP TABLE #temp1;

  IF EXISTS(SELECT * FROM sys.Tables WHERE name='#temp2')
  DROP TABLE #temp2;

  -- 定义一个开始之间.
  DECLARE @startDate AS DATETIME;

  -- 首先只查询一个字段的
  -- 设置 开始执行时间.
  SET @startDate = GETDATE();
  -- 执行查询.
  SELECT
    * INTO #temp1
  FROM
    TestDoc
  WHERE
    createDate >= CONVERT(DATETIME, '2008.01.01', 102)
    AND createDate < CONVERT(DATETIME, '2009.01.01', 102);
  -- 输出查询经过的时间.
  PRINT DATEDIFF(MS, @startDate, GETDATE());


  -- 然后查询多个字段的
  -- 设置 开始执行时间.
  SET @startDate = GETDATE();
  -- 执行查询.
  SELECT
    * INTO #temp2
  FROM
    TestDoc
  WHERE
    createDate >= CONVERT(DATETIME, '2008.01.01', 102)
    AND createDate < CONVERT(DATETIME, '2009.01.01', 102)
    AND owner LIKE '张三%';
  -- 输出查询经过的时间.
  PRINT DATEDIFF(MS, @startDate, GETDATE());

END
go



-- 用于测试执行性能的存储过程.
-- 该存储过程,在很大的数据量中,查询很大量的数据. [1/10]
CREATE PROCEDURE TestQuery3
AS
BEGIN
  SET NOCOUNT ON;

  IF EXISTS(SELECT * FROM sys.Tables WHERE name='#temp1')
  DROP TABLE #temp1;

  IF EXISTS(SELECT * FROM sys.Tables WHERE name='#temp2')
  DROP TABLE #temp2;

  -- 定义一个开始之间.
  DECLARE @startDate AS DATETIME;

  -- 首先只查询一个字段的
  -- 设置 开始执行时间.
  SET @startDate = GETDATE();
  -- 执行查询.
  SELECT
    * INTO #temp1
  FROM
    TestDoc
  WHERE
    createDate >= CONVERT(DATETIME, '1999.01.01', 102)
    AND createDate < CONVERT(DATETIME, '2009.01.01', 102);
  -- 输出查询经过的时间.
  PRINT DATEDIFF(MS, @startDate, GETDATE());


  -- 然后查询多个字段的
  -- 设置 开始执行时间.
  SET @startDate = GETDATE();
  -- 执行查询.
  SELECT
    * INTO #temp2
  FROM
    TestDoc
  WHERE
    createDate >= CONVERT(DATETIME, '1999.01.01', 102)
    AND createDate < CONVERT(DATETIME, '2009.01.01', 102)
    AND owner LIKE '张三%';
  -- 输出查询经过的时间.
  PRINT DATEDIFF(MS, @startDate, GETDATE());

END
go

-- 在没有索引的情况下:
EXECUTE TestQuery1;
GO
EXECUTE TestQuery2;
GO
EXECUTE TestQuery3;
GO

 


-- 创建非聚集索引
CREATE NONCLUSTERED INDEX idx_TestDoc ON TestDoc(createDate);
go

-- 使用非聚集索引的情况下
EXECUTE TestQuery1;
GO
EXECUTE TestQuery2;
GO
EXECUTE TestQuery3;
GO


-- 删除前面创建的索引
DROP INDEX idx_TestDoc ON TestDoc;
go

-- 创建新的聚集索引
CREATE CLUSTERED INDEX idx_TestDoc ON TestDoc(createDate);
go

-- 使用聚集索引的情况下
EXECUTE TestQuery1;
GO
EXECUTE TestQuery2;
GO
EXECUTE TestQuery3;
GO

 

最后的测试结果,大概的对比情况如下: 单位 毫秒

数据百分比  无索引    非聚集    聚集
1/36500     393-470   0-16      0-33
1/100       413-500   416-486   43-56
1/10        740-916   730-940   273-293


测试的结果显示:
在 返回大型结果集的查询 下
使用非聚集索引,性能比没有索引的性能好不了多少,可能还会更差。
使用聚集索引能够提高一定的性能。

来源:http://hi.baidu.com/wangzhiqing999/blog/item/ea57b6932325457854fb9634.html

猜你喜欢

转载自uule.iteye.com/blog/1330600
今日推荐