原文地址:

Stairway to SQL Server Indexes: Level 8,Unique Indexes

本文是SQL Server索引进阶系列(Stairway to SQL Server Indexes)的一部分。

本级别我们将测试唯一索引。唯一索引比较特别,不仅提高查询的性能,同时也带来数据完整性的好处。在SQL Server中,唯一索引是强制主键和候选键约束的唯一合理的方法。

唯一索引和约束

唯一索引不同于其他索引,入口不允许有相同的索引键值。因为索引的每个入口都会映射表中的一行,不允许相同的索引入口,也就是不允许表中存在相同的行。这就是为什么唯一索引是强制主键和候选键约束的。

定义主键约束或者唯一索引约束,SQL Server会自动创建索引。你可以只包含索引,没有约束;但是不能只有约束,没有索引。在定义约束的时候,就会创建一个和约束同名的索引。删除约束之后,才能删除索引,因为没有索引,约束就不能存在。删除约束之后,关联的索引也会被删除。

每张表可以有多个唯一索引。AdventureWork数据库的Product表有四个唯一索引,在ProductID,ProductNumber,rowguid和ProductName列上都有。AdventureWork的设计者选择ProductID作为主键,其他三个作为“替代键”,有时候也叫做“候选键”。

可以通过下面的语句创建唯一索引。

  1. CREATE UNIQUE NONCLUSTERED INDEX [AK_Product_Name]
  2. ON Production.Product ( [Name] );

或者是定义一个约束。

  1. ALTER TABLE Production.Product
  2. ADD CONSTRAINT PK_Product_ProductID PRIMARY KEY CLUSTERED
  3. (
  4. ProductID    );

在第一个列子中,你确保在产品表中没有相同的产品名称;第二个例子,确保产品表没有重复的ProductID。

因为定义主键约束或者是替代键约束的同时会创建索引,你必须在定义约束的时候指定必要的索引信息,就像ALTER TABLE中的CLUSTERED关键字。

如果表中包含违反约束或者索引限制的数据,在这样的表上使用create index语句会失败。

如果创建了索引,任何违反约束或者索引的数据操作都会失败。假设我们在Product表中查询相同名称的记录。

  1. INSERT Production.Product

  2. (

  3. Name, ProductNumber,

  4. Color,

  5. SafetyStockLevel, ReorderPoint, StandardCost,

  6. ListPrice, Size,

  7. SizeUnitMeasureCode, WeightUnitMeasureCode, [Weight],

  8. DaysToManufacture, ProductLine, Class, Style,

  9. ProductSubcategoryID, ProductModelID,

  10. SellStartDate, SellEndDate, DiscontinuedDate

  11. )

  12. VALUES

  13. (

  14. 'Full-Finger Gloves, M', 'A unique product number',

  15. 'Black',

  16. 4, 3, 20.00, 40.00, 'M',

  17. NULL, NULL, NULL,

  18. 0, 'M', NULL, 'U', 20, 3,

  19. GETDATE(), GETDATE(), NULL

  20. );

你会看到下面的错误信息。

  1. Msg 2601, LEVEL 14, State 1, Line 1
  2. Cannot INSERT duplicate KEY row IN object 'Production.Product'
  3. WITH UNIQUE INDEX  'AK_Product_Name'.
  4. The statement has been terminated.

消息告诉我们,AK_Product_Name索引阻止了我们插入已经存在的产品名称。

主键约束和唯一约束有一些不同:

  • 主键约束不允许NULL值,唯一约束允许NULL值。但是,唯一约束视两个NULL为重复值,因此唯一约束列中只能存在一个NULL值。
  • 创建主键约束,顺便会创建聚集索引,以下情况除外:
    • 表中已经包含聚集索引。
    • 创建约束的时候指定了NONCLUSTERED关键字。
  • 创建唯一约束,顺便会创建非聚集索引,除非创建的时候指定了CLUSTERED关键字,并且表还没有聚集索引。
  • 每张表只能包含一个主键约束,可以包含多个唯一约束。

在决定是创建唯一约束,还是只是一个唯一索引的时候,可以参考下面的一些指导规则:

唯一约束和唯一索引之间没有太大的区别。验证数据的方式相同,定义约束顺便创建的索引和直接创建的索引在查询优化方面是不同的。但是,如果数据完整性是目标的话,应该在列上创建唯一约束。这使得索引的目标很清晰。

合并唯一索引和过滤索引

在上面提到的唯一索引中,只允许一个NULL值,经常会和一些业务需求有冲突。通常,在一列上对已经存在的值,我们会强制唯一性,但是允许其他行的这一列没有值。

例如,你是一个产品的供应商,你是从第三方的供应商拿货,将产品信息保存在名为ProductDemo的表中。表中的ProductID列保存你自己设计的ID,还有一个UPC(Universal Product Code),不是所有的产品都有UPC。

表结构如下。


ProductID
UPCode Other Columns
(Primary Key) (Unique, but not a key)  
     
14AJ-W 036000291452  
23CZ-M    
23CZ-L    
18MM-J 044000865867  

在第二列中,你需要强制UPC唯一,同时允许NULL值。最好的办法是提供一个合并唯一索引和过滤索引的功能。(过滤索引在第七级中介绍过,是SQL Server 2008中引入的概念。)

创建表。

  1. CREATE TABLE ProductDemo
  2. (
  3. ProductID NCHAR(6)  NOT NULL PRIMARY KEY,
  4. UPCode    NCHAR(12) NULL
  5. );

创建唯一索引。

  1. CREATE UNIQUE NONCLUSTERED INDEX AK_UPCode on ProductDemo(UPCode) where UPCode!=null)

插入多条数据。

  1. INSERT ProductDemo (ProductID , UPCode) VALUES ('14AJ-W', '036000291452')
  2. , ('23CZ-M', NULL)
  3. , ('23CZ-L', NULL)
  4. , ('18MM-J', '044000865867');

但是,当我们插入重复UPC的时候。

  1. INSERT ProductDemo (ProductID , UPCode) VALUES ('14AJ-K', '036000291452');

我们收到了下面的提示信息。

  1. Msg 2601, Level 14, State 1, Line 1
  2. Cannot insert duplicate key row in object 'dbo.ProductDemo' with unique index 'AK_UPCode'.
  3. The statement has been terminated.

索引保持了数据的完整性,而且允许存在多行的NULL值。

在创建唯一索引的时候,可以使用 IGNORE_DUP_KEY选项,初始的创建索引语句应该是下面的样子。

  1. CREATE UNIQUE NONCLUSTERED INDEX AK_Product_Name
  2. ON Production.Product ( [Name] )
  3. WITH ( IGNORE_DUP_KEY = OFF );

这个选项的名字有一点误导,存在唯一索引的时候,不应该忽略重复键。更准确的说,在唯一索引中不允许重复键。这个选项只是在多行插入的时候,才是可用的,用来控制插入行为的。

举例来说,假如你有两张表TableA和TableB,有相同的表结构,你可以向SQL Server提交下面的插入语句。

  1. INSERT INTO TableA
  2. SELECT *
  3. FROM TableB;

SQL Server试图向TableA中拷贝TableB的所有记录。因为重复行,不能将TableB中的记录拷贝到TableA中,怎么办?你是想,只是重复行拷贝失败,还是整个insert语句失败呢?

这个选项是你来决定的。在创建唯一索引的时候,你已经做出决定,在insert的时候发生唯一键重复的处理办法。 IGNORE_DUP_KEY选项的解释如下:

IGNORE_DUP_KEY=OFF

整个insert语句会失败。会提示下面的错误信息。

NoteThis choice is the default.

IGNORE_DUP_KEY = ON

只有重复的行会插入失败。会提示下面的错误信息。

Note:  This choice cannot be used if the unique index is also a filtered index.

IGNORE_DUP_KEY选项只影响INSERT操作。会被update,create index和alter index语句忽略。IGNORE_DUP_KEY选项也可以在添加主键和唯一约束的使用使用。

为什么唯一索引能提供意想不到的好处

唯一索引能提供意想不到的好处。这是因为它们向SQL Server提供了我们想当然的信息,但是SQL Server却从来没有假设过。AdventureWork的Product表有两个唯一索引,ProductID和ProductName。

假设,你从仓库管理员哪里收到一个请求,查询需要显示下面的信息。

  • 产品名称
  • 这个产品被卖出的次数。
  • 这些订单的销售额。

你写出了下面的语句。

  1. SELECT [Name]
  2. , COUNT(*) AS 'RowCount'
  3. , SUM(LineTotal) AS 'TotalValue'
  4. FROM Production.Product P
  5. JOIN Sales.SalesOrderDetail D ON D.ProductID = P.ProductID
  6. GROUP BY p.Name

仓库管理员很高兴,他们看到了想要的结果。每行一个产品,每行都有产品名称,销售次数,总共的销售额。

Name                               RowCount    TotalValue
---------------------------------- ----------- ----------------------------------
Sport-100 Helmet, Red              3083        157772.394392
Sport-100 Helmet, Black            3007        160869.517836
Mountain Bike Socks, M             188         6060.388200
Mountain Bike Socks, L             44          513.000000
Sport-100 Helmet, Blue             3090        165406.617049
AWC Logo Cap                       3382        51229.445623
Long-Sleeve Logo Jersey, S         429         21445.710000
Long-Sleeve Logo Jersey, M         1218        115249.214976
Long-Sleeve Logo Jersey, L         1635        198754.975360
Long-Sleeve Logo Jersey, XL        1076        95611.197080
HL Road Frame - Red, 62            218         394255.572400
:

但是你关心的是这个查询潜在的消耗。在这个查询中SalesOrderDetail表要比Product表大,一定要按照产品名称分组,这个值在Product表中,而不在SalesOrderDetail表中。

使用SQL Server管理器,SalesOrderDetail表的主键是聚集的,SalesOrderID/SalesOrderDetailID,但是在用产品名称分组的时候根本没有用到。

如果使用第五级中提到的索引,在SalesOrderDetail表的ProductID外键上创建非聚集索引。

  1. CREATE NONCLUSTERED INDEX FK_ProductID_ModifiedDate
  2. ON Sales.SalesOrderDetail
  3. (
  4. ProductID,
  5. ModifiedDate
  6. )
  7. INCLUDE
  8. (
  9. OrderQty,
  10. UnitPrice,
  11. LineTotal
  12. );

你感觉这个索引应该对查询有帮助,索引中包含了查询的所有信息,除了产品名称,它在ProductID的序列中。但是你任然担心,用来分组的信息不在同一张表中,而在另外一张表中。

你返回SQL Server管理器,打开“显示实际的执行计划”选项,执行查询,看到了下面的执行计划。

你很惊奇的发现,AK_Product_Name索引没有用到,尽管ProductName是group by子句的聚合键。然后你认识到,在Product.Name上有一个唯一索引,在Product.ProductID上也有一个唯一索引,这说明每个ProductID只有一个产品,每个ProductName也只有一个产品。因此,在group by Name和group by ProductID没有区别,他们都是每个产品一个组。

SQL Server会同时扫描SalesOrderDetail表的非聚集索引和Product表的聚集索引 ,都是以ProductID的序列,生成每一组的总数,合并产品名称,不需要做排序和哈希。简单的说,SQL Server为你的查询生成了最高效的执行计划。

如果你删除Product.AK_Product_Name索引。

  1. IF EXISTS ( SELECT *
  2. FROM sys.indexes
  3. WHERE OBJECT_ID = OBJECT_ID(N'Production.Product')
  4. AND name = N'AK_Product_Name')
  5. DROP INDEX AK_Product_Name
  6. ON Production.Product;

新的执行计划效率会差一点,需要额外的排序和合并操作。

你会看到,尽管唯一索引的主要目的是提供数据的完整性,也可以帮助查询优化器决定获取数据的最优方法,即使在访问数据的时候没有用到索引。

结论

唯一索引为主键和替代键约束提供支持。唯一索引可能存在对应的唯一约束,但是没有索引约束就不存在。

唯一索引也可以是一个过滤索引。这就允许在一列中既要强制唯一,也可以有多个NULL值。

IGNORE_DUP_KEY选项影响多行插入的行为。

唯一索引提供较好的查询性能,即使在查询没有用到索引的情况下也可以。

代码下载

Level8.sql

转载于:https://blog.51cto.com/virusswb/987099

【翻译】SQL Server索引进阶:第八级,唯一索引相关推荐

  1. SQL Server 性能调优3 之索引(Index)的维护

    SQL Server 性能调优3 之索引(Index)的维护 热度1 评论 16 作者:溪溪水草 SQL Server 性能调优3 之索引(Index)的维护 前言 前一篇的文章介绍了通过建立索引来提 ...

  2. SQL server 系统优化--通过执行计划优化索引(1) (转)

    SQL server 系统优化--通过执行计划优化索引(1) 前几天,远离上海,到了温州,在客户的这边处理系统慢,该系统每天正常down机7次左右,在线人员一多,系统运行缓慢,严重影响业务操作,到了无 ...

  3. 面向初学者的 SQL Server 查询执行计划(1)——聚集索引运算符(Clustered Index)

    在本文中,我们将讨论与聚集索引相关的各种执行计划运算符,以及它们的作用.它们何时出现以及它们何时出现. 执行计划中的每一个运算符都会提供一些有关 SQL Server 运行方式的指标. 我们需要理解这 ...

  4. mysql 唯一索引和复合索引 区别_MySQL复合唯一索引分析

    MySQL复合唯一索引分析 关于复合唯一索引(unique key 或 unique index),网上搜索不少人说:"这种索引起到的关键作用是约束,查询时性能上没有得到提高或者查询时根本没 ...

  5. [翻译]SQL Server 该做和不该做的

    出处:http://www.codeproject.com/cs/database/sqldodont.asp 仅供学习省事用,翻译不准,详细了解请看原文. 假设你第一次主导一个基于SQL Serve ...

  6. sql查询初学者指南_面向初学者SQL Server查询执行计划–非聚集索引运算符

    sql查询初学者指南 Now that we understand what Clustered Index Scan and Clustered Index Seek are, how they o ...

  7. sql server 锁定_如何使用SQL Server 2014托管锁定优先级控制在线索引重建锁定

    sql server 锁定 When you perform a SQL Server Online Index Rebuild operation, introduced for the first ...

  8. SQL Server临界点游戏——为什么非聚集索引被忽略!

    当我们进行SQL Server问题处理的时候,有时候会发现一个很有意思的现象:SQL Server完全忽略现有定义好的非聚集索引,直接使用表扫描来获取数据.我们来看看下面的表和索引定义: 1 CREA ...

  9. 数据库原理与应用(SQL Server)笔记 第五章 索引和视图

    目录 前言 一.索引 (一)索引的概念 (二)索引的分类 (三)索引的操作 1.创建 2.查看 2.修改 3.删除 二.视图 (一)视图的概念 (二)视图的分类 (三)视图的操作 1.创建 2.查询 ...

  10. SQL SERVER中关于OR会导致索引扫描或全表扫描的浅析

    在SQL SERVER的查询语句中使用OR是否会导致不走索引查找(Index Seek)或索引失效(堆表走全表扫描 (Table Scan).聚集索引表走聚集索引扫描(Clustered Index ...

最新文章

  1. C++ RBTree(红黑树) 算法(附完整源码)
  2. nginx请求转发被拒绝_nginx反向代理(请求转发-URL匹配规则)
  3. mysql双重分组没有值也要显示_mysql 统计数据,按照日期分组,把没有数据的日期也展示出来...
  4. Java File类详解
  5. iOS 通用宏定义 高效全局宏汇总
  6. mysql myflash原理_如何利用 Myflash 解析 binlog ?
  7. 2021年中国电动吸引器市场趋势报告、技术动态创新及2027年市场预测
  8. java placeholder_Java占位符
  9. 讲讲金融业务(一)--自助结算终端POS
  10. SLAM_三维点优化时为什么使用逆深度误差而不是深度误差?
  11. 为什么有人把《海贼王》当作人生信条
  12. OpenLayers 官网例子的中文详解 1
  13. SCL编写的阀门块实例
  14. Ciphertext-Policy Attribute-Based Encryption (CP-ABE) Toolkit Installation
  15. ArcMap符号样式制作
  16. 九大数据分析方法:结构分析法
  17. Bt(宝塔面板)安装wordpress以及如何开启最简单实用的动静分离优化
  18. springboot--享学课堂
  19. java塑形是什么意思,健身中该减脂还是该塑形,所谓“塑形”是个什么概念?...
  20. 【飞桨PaddlePaddle学习心得】被遗忘的8张图片(day2爬虫的王姝慧的问题)

热门文章

  1. 文件系统管理 之 Linux 创建文件系统及挂载文件系统流程详解
  2. 文件系统的两种文件索引模式extent和blockmap
  3. 国庆七天乐——第一天
  4. TabBarItem图片不显示的原因
  5. 跨平台图表控件TeeChart使用教程:导入XML数据
  6. C# VB6.0 Java C++ GUID 生成
  7. SQL积累-SQL取得字符串中的文件名
  8. Smartmail外贸CRMBuild1.0版使用说明书
  9. 微信七年回顾:历经多少质疑和差评,才配拥有今天的强大
  10. linux下的apache、mysql自启动