PRIMARY KEYs与UNIQUE约束

时间:2012-02-10 20:17:37

标签: sql-server sql-server-2008 foreign-keys

Alexander Kuznetsov article中,他提供了以下代码段:

CREATE TABLE dbo.Vehicles(
    ID INT NOT NULL, 
    [Type] VARCHAR(5) NOT NULL,
    CONSTRAINT Vehicles_PK PRIMARY KEY(ID),
    CONSTRAINT Vehicles_UNQ_ID_Type UNIQUE(ID, [Type]),
    CONSTRAINT Vehicles_CHK_ValidTypes CHECK([Type] IN ('Car', 'Truck'))
);

这个片段为我提出了一些问题。

  1. 为什么有必要在唯一约束中包含IDType?如果只有ID是唯一的,那么两列的组合也将始终是唯一的。

  2. 此外,我知道如何设置主键并指定它在SSMS中是否唯一。但是,如何在一列上指定主键,并对列组合进行唯一约束?这会创建两个索引吗?

  3. 这是因为我试图实现类似的代码,它不会创建复合主键,我得到以下错误。所以我想更好地理解这段代码。

      

    表'MyTable'中的列与现有的主键或UNIQUE约束不匹配。


    修改

    我只需在MyTable中创建复合主键即可实现此功能。实际的表定义如下所示。再次,这是有效的。但它与上面引用的代码不同。而且我不确定如果我以另一种方式做到这一点会更好。

    CREATE TABLE [dbo].[MessageThread](
        [Id] [int] IDENTITY(1,1) NOT NULL,
        [MessageThreadType] [int] NOT NULL,
        CONSTRAINT [PK_MessageThread_1] PRIMARY KEY CLUSTERED 
        (
            [Id] ASC,
            [MessageThreadType] ASC
        ) WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
    ) ON [PRIMARY]
    GO
    
    ALTER TABLE [dbo].[MessageThread]  WITH CHECK ADD  CONSTRAINT [CK_MessageThread_ValidType] CHECK  (([MessageThreadType]=(2) OR [MessageThreadType]=(1)))
    GO
    
    ALTER TABLE [dbo].[MessageThread] CHECK CONSTRAINT [CK_MessageThread_ValidType]
    GO
    

5 个答案:

答案 0 :(得分:7)

根据我的理解,对ID,[Type]进行唯一约束的原因是让详细信息表将ID,[Type]称为外键。通常,父表需要对用于外键的列具有唯一约束。例如,问题中的表可以有2个详细信息表:

CREATE TABLE dbo.CARS( 
....
vehicle_id INT NOT NULL,
[Type] VARCHAR(5) NOT NULL,
CONSTRAINT CAR_CHK_TYPE CHECK [Type]='Car',
CONSTRAINT CAR_FK_VEHICLE FOREIGN KEY (vehicle_id,[Type]) REFERENCES Vehincle(id,[Type]));

CREATE TABLE dbo.TRUCKS( 
....
vehicle_id INT NOT NULL,
[Type] VARCHAR(5) NOT NULL,
CONSTRAINT CAR_CHK_TYPE CHECK [Type]='Truck',
CONSTRAINT CAR_FK_VEHICLE FOREIGN KEY (vehicle_id,[Type]) REFERENCES Vehincle(id,[Type]));

这种方式Cars仅包含Car类型的详细信息,而TRUCKS仅包含Truck

这种设计用于避免多态关系,例如

CREATE TABLE dbo.VEHICLE (
...,
ref_id INT NOT NULL,
-- PK of 'master' table
ref_name VARCHAR(20) NOT NULL, 
-- here we put 'truck' or 'car', so we virtually have 2 parents; 
-- in this case we cannot use FK constraint, the only thing that may 
-- somehow enforce the logical constraint is writing a trigger

<强>更新

您更新的表格定义对我来说很好。我想样本表最初是为Oracle设计的,然后移植到SQLServer。在Oracle中,该唯一约束和主键可以使用相同的索引,因此同时具有PK和Unique约束不会受到惩罚。

答案 1 :(得分:7)

1:我不确定给定模式的具体目的。但请注意,可以出于多种原因应用唯一约束,最常见的原因是:(a)强制执行唯一性;(b)为优化程序提供更多信息以作出基础决策。

2:唯一约束不会创建两个索引。它创建一个索引,其中一列作为前导键列。它强制执行两者的唯一性。因此对a,b的唯一约束可能有:

a    b    
---- ----
1    1
1    2
2    1
2    2

请注意,这两列都不会单独强制实现唯一性。我不是在SSMS中使用表设计器的忠实粉丝(它有大量的bug并且不支持所有功能)但是这里是如何做到的:

a)右键单击网格,然后选择Indexes/Keys...

b)使用Columns网格中的[...]按钮选择多个列

c)将Type更改为Unique Key

d)如果需要,更改Name

enter image description here

以下是已有主键的表的示例。如果我想要,我可以添加一个或多个唯一索引:

enter image description here

答案 2 :(得分:4)

  1. 好问题。理论上你是对的;没有理由,记录总是可以由PK唯一地识别,只要这是真的,就会始终满足唯一约束。但是,如果ID和Type在数据层的边界之外有一些关系(可能这个表是Enum的数据模型?),那么不太可能存在两个具有相同Type的不同ID,因为Type的唯一性是其他地方执行。约束还设置了一个包含ID和Type的索引,使得表格相对有效地被列组合查询。

  2. 使用“管理索引和键”选项设置唯一约束。是的,这将为主键创建索引和唯一约束,并为PK和Type的组合创建索引和唯一约束。

答案 3 :(得分:4)

我怀疑在UNIQUE约束中同时包含两列的原因与您提到的错误消息有关。 SQL Server(与其他SQL DBMS一样)有一个限制,即FOREIGN KEY约束只能引用唯一性约束定义的列集。因此,如果FOREIGN KEY约束引用了两列,那么这两列必须对它们具有唯一性约束 - 即使其他约束已经保证唯一性。这是一个毫无意义的限制,但它是标准SQL的一部分。

以下示例非常相似,并解释了为什么复合外键和嵌套唯一性约束可能有用。

http://consultingblogs.emc.com/davidportas/archive/2007/01/08/Distributed-Keys-and-Disjoint-Subtypes.aspx

答案 4 :(得分:4)

你走了:

  1. 汽车和卡车具有不同的属性,因此它们不属于一个表格。这就是为什么我有两张桌子,汽车和卡车。

  2. 然而,汽车和卡车有一些属性,例如VIN(车辆识别号码)。更重要的是,VIN是独一无二的。这就是为什么我需要一张桌子车。车辆既不能是汽车也不能是卡车,所以我必须确保不能同时输入(VIN = 123456789,Type = Car)和(VIN = 123456789,Type = Truck)。这就是我在VIN上只有PK的原因。

  3. 我必须确保车辆在汽车和卡车表中都没有相应的行。这就是我在汽车和卡车中使用Type列的原因,这就是为什么我想在子表中使用(VIN,Type)汽车和卡车引用父表Vehicle。我需要对(VIN,Type)附加唯一约束的唯一原因是:它由子表中的FK约束引用。

  4. 顺便说一句,你可以在博客上发表评论 - 在这种情况下,sqlblog会给我发消息。我在这里注意到你的问题是巧合;我应该去滑雪,只有没有雪。