【译注:此文为翻译,由于本人水平所限,疏漏在所难免,欢迎探讨指正】 

原文链接:传送门。

在本章节我们检查唯一索引。唯一索引的特别之处在于它不仅提供了性能益处,而且提供了数据完整性益处。在SQL SERVER中,唯一索引是保证主键约束和候选键约束的唯一合理的方式。

唯一索引和约束

        唯一索引与其他任何索引并没有什么不同,唯一不同之处在于它不允许多个条目具有相同的索引键值。既然索引中的一个条目映射到了交互表的一行,那么阻止条目被加入到索引中便是阻止了数据行被加到表中。这便是为什么唯一索引是主键约束和候选键约束的保证。

声明一个主键或者唯一性索引都会导致SQL SERVER自动创建索引。你可以具有一个唯一索引而没有与之匹配的约束,但是没有唯一索引的话,你不能具有任何唯一约束。定义一个约束会导致与约束同名的一个索引被创建。不首先删除约束,你是不能删除索引的,因为约束是不能离开索引而存在的。删除约束也会导致与之关联的索引被删除。

每张表包含超过一个唯一索引是可能的。举个例子,AdventureWork库的Product 表具有四个唯一索引,ProductID, ProductNumber, rowguid, ProductName这四个列各有一个唯一索引。AdventureWorks 数据库的设计者选择ProductID 作表的主键,其他三个作替换键,有时候 称为候选键。

你能够用CREATE INDEX 语句创建一个唯一索引,如下所示:

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

View Code

也可以通过定义约束来创建唯一索引,如下所示:

ALTER TABLE Production.ProductADD CONSTRAINT PK_Product_ProductID PRIMARY KEY CLUSTERED(ProductID    );

View Code

在第一个例子中,你将确保不会有两个商品会有相同的商品名,在第二个例子中, 你将确保没有两个商品会有相同的ProductID 值。因为定义一个主键约束或者候选键约束会导致一个索引被创建,因此在约束的定义中你必须指定必要的索引信息,因此CLUSTERED关键字出现在上述ALTER TABLE 子句中。如果表包含有违反约束后者违反索引限制的数据,那么“CREATE INDEX ”语句会失败。

如果索引能够被创建,那么后续任何会 违反约束或索引的DML都会失败。举个例子,假设我们尝试着插入一条具有重复商品名的数据行,插入语句如下所示:

INSERT Production.Product
(
Name, ProductNumber,
Color,
SafetyStockLevel, ReorderPoint, StandardCost,
ListPrice, Size,
SizeUnitMeasureCode, WeightUnitMeasureCode, [Weight],
DaysToManufacture, ProductLine, Class, Style,
ProductSubcategoryID, ProductModelID,
SellStartDate, SellEndDate, DiscontinuedDate
)
VALUES
(
'Full-Finger Gloves, M', 'A unique product number',
'Black',
4, 3, 20.00, 40.00, 'M',
NULL, NULL, NULL,
0, 'M', NULL, 'U', 20, 3,
GETDATE(), GETDATE(), NULL
);列表1:插入重复的商品名

View Code

这条语句会执行失败,我们会收到如下的错误信息:

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

这条信息告知我们AK_Product_Name 索引成功的保护了我们的表,它阻止了一个包含已经存在的商品名的数据行的非法插入。

在主键约束和唯一索引之间存在着一些细微的差异:

  • 主键约束禁止null值,没有任何索引条目的索引键允许包含null值。而唯一性索引却允许null值。然而,既然唯一性索引认为两个null值是相互重复的,在每列中,只有一个包含null值的检索键值可以在索引中存在。
  • 创建一个主键约束会导致创建一个聚集索引,除非以下事情发生:
  1. 表已经是一个聚集索引。
  2. 当你定义约束时候指定了NONCLUSTERED 关键字。
  • 当你创建唯一约束并且表不是聚集索引时,创建唯一性约束会导致创建一个非聚集索引,除非你指定了CLUSTERED 关键字。
  • 每张表只能有一个主键约束,但是可以有多个唯一性约束。

当决定是否创建一个唯一约束或者仅仅只是创建一个唯一索引时,请遵从MSDN库中的SQL SERVER文档:

“There are no significant differences between creating a UNIQUE constraint and creating a unique index that is independent of a constraint. Data validation occurs in the same manner, and the query optimizer does not differentiate between a unique index created by a constraint or manually created. However, you should create a UNIQUE constraint on the column when data integrity is the objective. This makes the objective of the index clear.”

组合使用唯一索引和过滤索引

我们在上面提到的唯一索引的属性:它们只允许存在一个null值,通常与通用的商业规则会有冲突。通常我们会想要保证一个列中已经存在的值是唯一的,而允许那一列多行数据都可以没有任何值。

举例来说,假设你是某个商品供应商,你会从第三方厂商那边得到某些商品,并且你将自己的产品信息保存在一张ProductDemo表中,你为所有的商品都分配了一个ProductID 值,同时你也保留了商品的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  

表1:ProductDemo表的部分内容

在第二列,你必须要确保UPC列的唯一性同时还需要允许null值的存在,提供这种功能的最好方式是组合使用唯一索引与过滤索引(过滤索引是第七章的主题,它在SQL SERVER 2008被引入)。

为了演示这个,我们创建了一个简单的表,其包含上面显示的列:

CREATE TABLE ProductDemo(ProductID NCHAR(6)  NOT NULL PRIMARY KEY,UPCode    NCHAR(12) NULL);

View Code

现在我们向其插入4条空UP Code行,那么四条数据会被加入到表中,如下所示:

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

View Code

然而当我们试着插入一行具有重复UPCode 值的数据行:

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

我们接收到了如下错误信息并且数据行没有被插入。

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

如同在本章之前的例子所做的那样,你可以选择指定IGNORE_DUP_KEY 选项。因此,我们原始的 CREATE INDEX 语句没有经过措辞:

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

这个选项的名称使人产生一些误解,因为当存在唯一索引时,重复的键值从来不会被忽略。更确切的说,唯一索引从来不会允许重复的键值。这个选项控制着插入多行数据时候可执行的行为。举个例子,你有两张表:表A和表B,它们有相同的表架构,那么你可以向SQL SERVER提交如下插入语句:

INSERT INTO TableA
SELECT *
FROM TableB;

SQL SERVER会尝试复制所有表B的数据行到表A,如果 表B的两条数据因为重复的值而不能被复制到表A会怎么样呢?你是希望其他所有行被复制,只有重复的两行失败呢,还是希望整个插入语句都会失败?

这个选择将由你来做。当你创建唯一索引时候,你将决定发生什么,当一个插入语句企图向一个唯一索引中插入多个重复键值时。IGNORE_DUP_KEY 的两个设置列举如下:

  • IGNORE_DUP_KEY = OFF

整个插入语句都会失败,并列出一个错误信息,注意,此选项是默认的。

  • IGNORE_DUP_KEY = ON

只有与已存在的行索引键值重复的行才会失败,并列出一个告警信息,注意,如果一个唯一索引同时又是过滤索引那么这个选项不能被设置

IGNORE_DUP_KEY 这个选项仅仅影响INSERT 语句,它会被UPDATE,CREATE INDEX, ALTER INDEX 语句忽略,当给一个表添加主键索引或者唯一索引时,也可以指定IGNORE_DUP_KEY 选项。

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

唯一索引能提供意料之外的性能益处。这是因为它们提供给SQL SERVER一些我们视为理所当然而SQL SERVER绝不会假设的信息,AdventureWork数据库Product 表的两个唯一索引:ProductID 和ProductName提供了证明了这个例子。

假设你收到了一个来自仓库员工的请求,这个查询会展示给他们一些Product 表中各个产品的如下信息:

  • 产品名
  • 产品被出售的次数
  • 这些销售的总价值

为了解决此问题,你写出了如下查询:

SELECT [Name], COUNT(*) AS 'RowCount', SUM(LineTotal) AS 'TotalValue'FROM Production.Product PJOIN Sales.SalesOrderDetail D ON D.ProductID = P.ProductID GROUP BY ProductID

View Code

仓库员工非常开心看到这个查询,因为它给了他们想要的结果。每个产品一行数据,每行包含了产品名,销售次数和总销售额。部分输出如下所示:

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 表,并不在SalesOrderDetail 表中。通过SSMS,你注意到SalesOrderDetail 表在它的主键上是聚集的(SalesOrderID / SalesOrderDetailID),当试着按产品名称分组时候这并不会产生任何益处。

如果你运行第五章的代码:包含列,你在SalesOrderDetail.ProductID 外键上创建了如下的非聚集索引:

CREATE NONCLUSTERED INDEX FK_ProductID_ModifiedDate
ON Sales.SalesOrderDetail
(
ProductID,
ModifiedDate
)
INCLUDE
(
OrderQty,
UnitPrice,
LineTotal
);

View Code

你感觉这个索引会对你的查询有所帮助,因为它包含了你的查询需要的所有信息,除了产品名之外,它按ProductID 的顺序排列,但是你仍关心于把一个表的信息按照另一个不同表的值进行分组。

你返回到SSMS,打开“显示实际的执行计划”选项,运行查询,注意到执行计划显示如下图:

首先你很惊奇于看到Product表的产品名称索引,Product.AK_Product_Name从来没有使用过,即使它的列是GROUP BY子句的聚合键。那么你意识到 Product.Name有一个唯一索引和 Product.ProductID有一个唯一索引会告知SQL SERVER每一个product name和每一个product id都会有一个产品,因此e, GROUP BY [Name] 或者GROUP BY ProductID 是一样的分组,也就是说,它们都会一个产品产生一个分组。

因此查询优化器认为你的查询和如下的查询是相同的:

SELECT[Name]
, COUNT(*)AS 'RowCount'
, SUM(LineTotal)AS 'TotalValue'
FROM Production.Product P
JOIN Sales.SalesOrderDetail D OND.ProductID= P.ProductID
GROUP BY ProductID

View Code

因此两个ProductID索引会同时支持对所请求数据的连接和分组操作。

SQL SERVER 能够同时扫描SalesOrderDetail 表上的覆盖索引和Product 表的聚集索引,它们两者都是以ProductID的顺序排列的,产生每个分组的总数,然后以产品名称合并,并不需要做任何排序或者哈希,简而言之,SQL SERVER为你的查询产生尽可能高效的执行计划。

如果你移除Product.AK_Product_Name 索引,像这样:

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

View Code

那么新的执行计划(如下所示)并不高效,它需要额外的排序和合并操作。

你能够看到尽管唯一索引的主要目的是提供了数据的一致性,它也能够帮助查询优化器决定收集数据的最高效的方式,即使那个索引并不是设计用来访问数据的。

结论

唯一索引为主键约束和候选键约束提供支持。唯一索引可以与其交互的约束一起存在,但唯一约束不能离开它的索引而存在。

唯一索引可以同时是一个过滤索引(filtered index),这保证了唯一性的列可以允许多个null值存在。

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

唯一索引能够提供更好的查询性能,即使索引并没有被使用。

转载于:https://www.cnblogs.com/qianxingmu/p/10485077.html

【译】索引进阶(八):SQL SERVER唯一索引相关推荐

  1. T-SQL查询进阶--理解SQL Server中索引的概念,原理以及其他(看了两次了,转了)

    简介 在SQL Server中,索引是一种增强式的存在,这意味着,即使没有索引,SQL Server仍然可以实现应有的功能.但索引可以在大多数情况下大大提升查询性能,在OLAP中尤其明显.要完全理解索 ...

  2. SQL Server的索引碎片

    无论是什么关系型数据库,尤其在OLTP系统中,索引是提升数据访问速度的常用方式之一,但是不同类型的数据库,对索引碎片的处理可能会略有不同. 我们知道,索引碎片多了,意味着可能会扫描更多的数据块.消耗更 ...

  3. 【译】SQL Server索引进阶第八篇:唯一索引

    索引设计是数据库设计中比较重要的一个环节,对数据库的性能其中至关重要的作用,但是索引的设计却又不是那么容易的事情,性能也不是那么轻易就获取到的,很多的技术人员因为不恰当的创建索引,最后使得其效果适得其 ...

  4. SQL Server索引进阶第十篇:索引的内部结构

    索引设计是数据库设计中比较重要的一个环节,对数据库的性能其中至关重要的作用,但是索引的设计却又不是那么容易的事情,性能也不是那么轻易就获取到的,很多的技术人员因为不恰当的创建索引,最后使得其效果适得其 ...

  5. SQL Server创建索引

    什么是索引 拿汉语字典的目录页(索引)打比方:正如汉语字典中的汉字按页存放一样,SQL Server中的数据记录也是按页存放的,每页容量一般为4K .为了加快查找的速度,汉语字(词)典一般都有按拼音. ...

  6. 关于SQL Server中索引使用及维护简介

    一.聚簇索引(clustered indexes)的使用 聚簇索引是一种对磁盘上实际数据重新组织以按指定的一个或多个列的值排序.由于聚簇索引的索引页面指针指向数据页面,所以使用聚簇索引查找数据几乎总是 ...

  7. SQL Server 聚集索引 clustered index 非聚集索引Nonclustered Indexes键查找查找Key Lookup执行计划过程详解

    SQL Server 聚集索引非聚集索引键查找过程详解 索引的相关术语 1 堆(Heap)是一种没有指定排序的数据结构,通俗的理解堆就像是按照顺序排放的杂物.在数据库里也即是对应没有聚集索引. 2 聚 ...

  8. 翻译:SQL Server中的索引内部结构:到SQL Server索引级别10的阶梯。

    SQL Server中的索引内部结构:到SQL Server索引级别10的阶梯. 大卫•杜兰特2012/01/20 该系列 本文是楼梯系列的一部分:SQL Server索引的阶梯. 索引是数据库设计的 ...

  9. 使用 SQL Server 2000 索引视图提高性能1

    什么是索引视图? 许多年来,Microsoft SQL Server" 一直都提供创建虚拟表(称为视图)的功能.在过去,这些视图主要有两种用途: 提供安全机制,将用户限制在一个或多个基表中的 ...

最新文章

  1. 四种可能导致数据中心瘫痪的网络攻击
  2. python 消息队列 sqlite_sqlite 数据存储 消息队列
  3. es like模糊匹配_es 基于match_phrase/fuzzy的模糊匹配原理及使用
  4. 移动端https抓包那些事--进阶篇
  5. html 窗口大小改变,html – 调整窗口大小时缩放整个身体
  6. 谷歌网络代理工具_谷歌更新了Dialogflow AI引擎,帮助客户创建更好的虚拟代理...
  7. app 应用商店系统php,GitHub - ChhXin/appstore-admin: 基于ThinkPHP的应用商店后台管理系统...
  8. 注解@controller的作用_@controller和@Restontroller区别
  9. 韭菜财经大数据:《2019年内容创业市场报告》
  10. 九度oj 题目1028:继续畅通工程
  11. 使用Python进行数独求解(一)
  12. 2022年各省高考查分时间、入口汇总
  13. PS画虚线的几种方法
  14. nginx静态图片路径映射
  15. 爬虫实例二:爬取拉勾网招聘信息
  16. 蓝桥杯web:3.【功能实现】封装函数实现个人所得税计算器
  17. ZooKeeper源码分析之完整网络通信流程(一)
  18. Parasoft全面发布最广泛的MISRA规则覆盖
  19. Unity中利用LineRenderer绘制寻路路径
  20. for 循环的流程图等价形式

热门文章

  1. 2060显卡驱动最新版本_聊一款现阶段性价比爆炸的显卡——铭瑄RTX2070 SUPER 电竞之心 OC...
  2. WEB前端开发职业学习路线初级完整版
  3. 数据和access数据的区别_Access处理数据
  4. mipi协议_Cadence发布业界首款面向多协议PHY的验证IP产品
  5. mysql半连接_MySQL优化案例:半连接(semi join)优化方式导致的查询性能低下(转载)...
  6. python写音乐_Python国产库推荐之musicpy:让你用代码来写音乐
  7. 服务器运维应该报什么专业,数据中心基础设施运维人员应该掌握哪些专业技能?...
  8. STM32F103单片机RTC实时时钟的使用
  9. Pytorch:线性代数
  10. 为了学习Rxjava,年轻小伙竟作出这种事!