데이터 베이스 MySQL 튜토리얼 mssql 唯一索引和约束 讲述

mssql 唯一索引和约束 讲述

Jun 07, 2016 pm 05:49 PM
고유 인덱스

唯一索引和其它索引本质上并没有什么不同,唯一不同的是唯一索引不允许索引键中存在相同的值。因为索引中每一个条目都与表中的行对应。

唯一索引和约束
    唯一索引不允许重复值被插入索引也就保证了对应的行不允许被插入索引所在的表,这也是为什么唯一索引能够实现主键和候选键。

    为表声明主键或唯一约束时,SQL Server会自动创建与之对应的唯一索引。你可以在没有唯一约束的情况下创建唯一索引,但反之则不行。定义一个约束时,SQL Server会自动创建一个与之同名的索引,并且你不能在删除约束之前删除索引。但可以删除约束,删除约束也会导致与之关联的索引被删除。

    每个表中可以包含多个唯一索引。比如说AdventureWorks的Product表,含有四个唯一索引,分别是ProductID,ProductNumber,rowguid和ProductNameColumn,设置Product表的人将ProductID作为主键,其它三个作为候选键。

     你可以通过Create INDEX语句创建唯一索引,比如:

 代码如下 复制代码

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

      也可以通过直接定义约束创建唯一索引:

 代码如下 复制代码

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

 

    上面第一种方法,你Prodcut表中不能含有相同的ProductName,第二种情况表中不允许存在相同的ProductID。

    因为定义一个主键或是定义约束会导致索引被创建,所以你必须在约束定义时就给出必要的索引信息,因此上面ALTER TABLE语句中包含了”CLUSTERED”关键字。

    如果唯一索引或约束所约束的列在当前的表中已经含有了重复值,那么创建索引会失败。

    而当唯一索引创建成功后,所有违反这个约束的DML语句都会失败,比如,我们打算加入一条当前表中存在的的ProductName,语句如下:

 代码如下 复制代码

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.插入的行和表中存在相同的ProductName

 上面代码执行后我们可以看到如下报错信息:

消息 2601,级别 14,状态 1,第 1 行 不能在具有唯一索引 'AK_Product_Name' 的对象
'Production.Product' 中插入重复键的行。 语句已终止。

 

上面的消息告诉我们AK_Product_Name索引不允许我们插入的数据含有和当前表中一样的ProductName。

 主键,唯一约束和没有约束
    主键约束和唯一约束有如下细小的差别。


•    主键约束不允许出现NULL值。任何索引的索引键都不允许包含null值。但唯一约束允许包含NULL值,但唯一约束把两个NULL值当作重复值,所以施加了唯一约束的每一列只允许包含一个NULL值。
•    创建主键时会自动创建聚集索引,除非当前表中已经含有了聚集索引或是创建主键时指定了NONCLUSTERED关键字。
•    创建唯一约束时会自动创建非聚集索引,除非你指定了CLUSTERED关键字并且当前表中还没有聚集索引。
•    每个表中只能有一个主键,但可以由多个唯一约束。
 

    对于唯一约束和唯一索引的选择,请参照MSDN上的指导,如下:

    唯一约束和唯一索引并没有显著的区别。创建独立的唯一索引和使用唯一约束对于数据的验证方式并无区别。查询优化器也不会区分唯一索引是由约束创建还是手工创建。然而以数据完整性为目标的话,最好创建约束,这使得对应的索引的目标一目了然。

 混合唯一索引和过滤索引
     上面我们提到过唯一索引只允许一个NULL值,但这和常见的业务需求有冲突。很多时候我们对于已经存在的值不允许重复,但是允许存在多个没有值的列。

    比如说吧,你是一个供货商,你所有的产品都来自于第三方厂商。你将你这里所有的商品信息都存在一个叫做ProductDemo的表中。你有自己的ProductID,还追踪产品的UPC(Universal Product Code)值。但并不是所有的厂商产品都存在UPC,你表中的部分数据如下所示。

ProductID UPCode Other Columns
主键 唯一索引  
     
14AJ-W 036000291452
  
23CZ-M    
23CZ-L    
18MM-J 044000865867
  

表1.ProductDemo表的部分内容

在上表中第二列,你既要保证UPCode的唯一性,又要保证允许NULL值。实现这种需求最好的办法就是混合唯一索引和过滤索引(过滤索引实在SQL Server 2008中引入的)。

    作为演示,我们创建了表1所示的表.

 代码如下 复制代码

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

 接下来我们插入如上所示的数据.

 代码如下 复制代码

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

 当我们插入重复值时

 代码如下 复制代码

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

    收到如下错误

消息 2601,级别 14,状态 1,第 1 行 不能在具有唯一索引 'xx' 的对象 'dbo.ProductDemo' 中插入重复键的行。
语句已终止。


   (译者注,这里原文作者应该是疏忽了,略坑爹,因为他没有创建过滤唯一索引,所以按照原文不会报错,我在这里加上了,代码:CREATE UNIQUE NONCLUSTERED INDEX xx on ProductDemo(UPCode) where UPCode!=null)

 选择合适的IGNORE_DUP_KEY选项
 

    当你创建唯一索引时,你可以指定IGNORE_DUP_KEY选项,因此本文最开始创建唯一索引的选项可以是:

 代码如下 复制代码

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

      IGNORE_DUP_KEY这个名字容易让人误会。唯一索引存在时重复的值永远不会被忽略。更准确的说,唯一索引中永远不允许存在重复键。这个选项的作用仅仅是在多列插入时有用。

    比如,你有两个表,表A和表B,有着完全相同的结构。你可能提交如下语句给SQL Server。

 代码如下 复制代码

INSERT INTO TableA SELECT * FROM TableB;

     SQL Server会尝试将所有表B中的数据插入表A。但如果因为唯一索引拒绝表B中含有和表A相同的数据插入A怎么办?你是希望仅仅重复数据插入不成功,还是整个INSERT语句不成功?

    这个取决于你设定的IGNORE_DUP_KEY参数,当你创建唯一索引时,通过设置设个参数可以设定当插入不成功时怎么办,设置IGNORE_DUP_KEY的两种参数解释如下:

    IGNORE_DUP_KEY=OFF

    整个INSERT语句都不会成功并弹出错误提示,这也是默认设置。

 

IGNORE_DUP_KEY=OFF

    只有那些具有重复键的行不成功,其它所有的行会成功。并弹出警告信息。

     IGNORE_DUP_KEY 选项仅仅影响插入语句。而不会被UPDATE,CREATE INDEX,ALTER INDEX所影响。这个选项也可以在设置主键和唯一约束时进行设置。

   为什么唯一索引可以提供额外的性能提升
    唯一索引可以提供出乎你意料之外的性能提升。这是因为唯一索引给SQL Server提供了确保某一列绝对没有重复值的信息。adventureWork的Product表中的ProductID和ProductName这两个唯一索引,提供了很好的例子。

    加入,你们公司数据仓库的某个哥们希望你给他提供Product表的一些信息,要求如下:

•    产品名称
•    产品销售的数量
•    总销售额
    因此,你写了如下的查询语句:

 代码如下 复制代码

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

 

    (译者注,这里原作者给的代码有问题,ProductID替换为P.Name)

   数据仓库的哥们对你的查询语句很满意,每一行都包含了产品名称,销售数量和总的销售额,查询出来的部分结果如下:

       但是,你对于这个查询的成本有所担心。SalesOrderDetail是上面查询中两个表中比较大的表,并且还按照ProductName进行分组,这个ProductName是来自Product表而不是SalesOrderDetail表。

    通过SQL Server Management Studio,你注意到SalesOrderDetail表有主键,并且主键也是聚集索引键,也就是SalesOrderID和SalesOrderDetailID,这个主键并不会给按照ProductName分组带来性能提升。

    如果你运行了第五篇包含列的代码,你创建了如下非聚集索引。

 代码如下 复制代码

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

    你觉得这个索引可以对你的查询有帮助因为这个索引包含了除了ProductName列的所有查询所需的信息。并且这个索引是按照ProductID进行排序的,但你仍然担心分组的ProductID来自其中一个表而Select的信息来自另一个表。

    你通过SQL Server Management Studio,通过查看执行计划,看到前面数据仓库那哥们要的查询的执行计划如图1所示。

.按Product.Name进行分组时的执行计划

     首先你可以惊讶于Product表的Product name索引,Product.AK_Product_Name没有被使用.然后你意识到在Product.Name列上和Product.ProductID上有唯一索引,这使得SQL Server知道这两列是唯一的。因此,Group By Name等效于Group By ProductID。这使得一个产品一个组。

    因此,查询优化器意识到你的查询等同于如下查询,这两个ProductID索引因此支持对所求查询的Join和group操作。

 代码如下 复制代码

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

    SQL Server会同时扫描SalesOrderDetail上的覆盖索引和聚集索引,这两个索引都是以ProductID进行排序的。因此使用合并连接,而免去了排序或散列操作,总之SQL Server生成了最有效的查询计划。

    如果你Drop了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;

那么生成的新的执行计划就没有那么有效了,需要额外的排序和合并连接操作。

图2.当Drop掉索引后,按照Product Name进行分组的查询的执行计划

你可以看到,虽然唯一索引的主要功能是保证数据的完整性,还可以帮助查询优化器生成更好的查询计划,即使这个索引本身不被用来访问数据。

总结
    唯一索引为主键和候选键提供了约束。唯一索引可以在没有唯一约束时存在,反之则不行。

     唯一索引同时也可以是过滤索引,这使得唯一索引可以允许一列中有多个NULL值。

    IGNORE_DUP_KEY 关键字可以影响批量插入语句。

    唯一索引还可以提供更好的性能,既然唯一索引本身并没有用于数据访问

본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

AI Hentai Generator

AI Hentai Generator

AI Hentai를 무료로 생성하십시오.

인기 기사

R.E.P.O. 에너지 결정과 그들이하는 일 (노란색 크리스탈)
3 몇 주 전 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. 최고의 그래픽 설정
3 몇 주 전 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. 아무도들을 수없는 경우 오디오를 수정하는 방법
3 몇 주 전 By 尊渡假赌尊渡假赌尊渡假赌

뜨거운 도구

메모장++7.3.1

메모장++7.3.1

사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전

SublimeText3 중국어 버전

중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기

스튜디오 13.0.1 보내기

강력한 PHP 통합 개발 환경

드림위버 CS6

드림위버 CS6

시각적 웹 개발 도구

SublimeText3 Mac 버전

SublimeText3 Mac 버전

신 수준의 코드 편집 소프트웨어(SublimeText3)

Alter Table 문을 사용하여 MySQL에서 테이블을 어떻게 변경합니까? Alter Table 문을 사용하여 MySQL에서 테이블을 어떻게 변경합니까? Mar 19, 2025 pm 03:51 PM

이 기사는 MySQL의 Alter Table 문을 사용하여 열 추가/드롭 테이블/열 변경 및 열 데이터 유형 변경을 포함하여 테이블을 수정하는 것에 대해 설명합니다.

MySQL 연결에 대한 SSL/TLS 암호화를 어떻게 구성합니까? MySQL 연결에 대한 SSL/TLS 암호화를 어떻게 구성합니까? Mar 18, 2025 pm 12:01 PM

기사는 인증서 생성 및 확인을 포함하여 MySQL에 대한 SSL/TLS 암호화 구성에 대해 설명합니다. 주요 문제는 자체 서명 인증서의 보안 영향을 사용하는 것입니다. [문자 수 : 159]

MySQL에서 큰 데이터 세트를 어떻게 처리합니까? MySQL에서 큰 데이터 세트를 어떻게 처리합니까? Mar 21, 2025 pm 12:15 PM

기사는 MySQL에서 파티셔닝, 샤딩, 인덱싱 및 쿼리 최적화를 포함하여 대규모 데이터 세트를 처리하기위한 전략에 대해 설명합니다.

인기있는 MySQL GUI 도구는 무엇입니까 (예 : MySQL Workbench, Phpmyadmin)? 인기있는 MySQL GUI 도구는 무엇입니까 (예 : MySQL Workbench, Phpmyadmin)? Mar 21, 2025 pm 06:28 PM

기사는 MySQL Workbench 및 Phpmyadmin과 같은 인기있는 MySQL GUI 도구에 대해 논의하여 초보자 및 고급 사용자를위한 기능과 적합성을 비교합니다. [159 자].

드롭 테이블 문을 사용하여 MySQL에서 테이블을 어떻게 드롭합니까? 드롭 테이블 문을 사용하여 MySQL에서 테이블을 어떻게 드롭합니까? Mar 19, 2025 pm 03:52 PM

이 기사에서는 Drop Table 문을 사용하여 MySQL에서 테이블을 떨어 뜨리는 것에 대해 설명하여 예방 조치와 위험을 강조합니다. 백업 없이는 행동이 돌이킬 수 없으며 복구 방법 및 잠재적 생산 환경 위험을 상세하게합니다.

외국 키를 사용하여 관계를 어떻게 표현합니까? 외국 키를 사용하여 관계를 어떻게 표현합니까? Mar 19, 2025 pm 03:48 PM

기사는 외국 열쇠를 사용하여 데이터베이스의 관계를 나타내고 모범 사례, 데이터 무결성 및 피할 수있는 일반적인 함정에 중점을 둡니다.

JSON 열에서 인덱스를 어떻게 생성합니까? JSON 열에서 인덱스를 어떻게 생성합니까? Mar 21, 2025 pm 12:13 PM

이 기사에서는 PostgreSQL, MySQL 및 MongoDB와 같은 다양한 데이터베이스에서 JSON 열에서 인덱스를 작성하여 쿼리 성능을 향상시킵니다. 특정 JSON 경로를 인덱싱하는 구문 및 이점을 설명하고 지원되는 데이터베이스 시스템을 나열합니다.

일반적인 취약점 (SQL 주입, 무차별 적 공격)에 대해 MySQL을 어떻게 보호합니까? 일반적인 취약점 (SQL 주입, 무차별 적 공격)에 대해 MySQL을 어떻게 보호합니까? Mar 18, 2025 pm 12:00 PM

기사는 준비된 명령문, 입력 검증 및 강력한 암호 정책을 사용하여 SQL 주입 및 무차별 적 공격에 대한 MySQL 보안에 대해 논의합니다 (159 자)

See all articles