PK和唯一约束

时间:2013-04-29 11:38:34

标签: sql-server

我有一个表来保存用户声明如下(1):

CREATE TABLE [dbo].[UserClaims] (
  [UserAccountID] [int] NOT NULL,
  [Type] [nvarchar](150) NOT NULL,
  [Value] [nvarchar](150) NOT NULL,
  CONSTRAINT [PK_dbo.UserClaims] PRIMARY KEY ([UserAccountID], [Type], [Value])
)

我有同一张桌子的第二个选项(2):

CREATE TABLE [dbo].[UserClaims] (
  [UserAccountID] [int] NOT NULL,
    CONSTRAINT PK_UserClaims_UserAccountId PRIMARY KEY CLUSTERED (UserAccountId),
  [Type] [nvarchar](150) NOT NULL,
  [Value] [nvarchar](150) NOT NULL,
    CONSTRAINT UQ_UserClaims_Value_Type_UserAccountID unique (Value, [Type], UserAccountID)
)

我有桌子:

CREATE TABLE [dbo].[UsersAccounts] (
  [UserAccountID] [int] IDENTITY NOT NULL,
    CONSTRAINT PK_UsersAccounts_UserAccountId PRIMARY KEY CLUSTERED (UserAccountId)
)

当我收到用户时,我总是需要从UserClaims获得所有声明。

UserAccountID足以识别所有用户声明...

在选项(2)中,UserAccountID既是PK又是FK。

我需要能够将以下行添加到UserClaims:

UserAccountID = 2,Type = Role,Value = Editor

UserAccountID = 1,Type = Role,Value = Editor

UserAccountID = 1,Type = Role,Value = Member

UserAccountID = 1,Type = Name,Value = John

但我无法添加以下声明:

UserAccountID = 1,Type = Role,Value = Editor

UserAccountID = 1,Type = Role,Value = Editor

我不能让两行完全相等。所有其他组合都是可能的。

我的想法是使用(2)。它比我以前更接近......你怎么看?

谢谢!

6 个答案:

答案 0 :(得分:3)

这一切都取决于你应该是什么主键:) 但很可能[UserAccountID]应该是主键,所以第一个解决方案是错误的。它允许您插入多个具有相同[UserAccountID](不同于其他两列)的行,这将在与其他表的连接上生成双倍记录。

答案 1 :(得分:2)

第二个更有用,因为你可能会在WHERE和JOIN中使用UserAccountID,所以它上面的索引会让你的查询更有效率。

答案 2 :(得分:2)

虽然在强制执行的约束方面,PKUNIQUE都强制列(或列集)具有唯一值。但从逻辑上讲,primary key是允许您唯一标识记录的那个,而不仅仅是维护列上的唯一约束。表中只能有一个PK,但可以是多个唯一键。

更多关于使用情况:

例如,你有一个表person (varchar ssn, varchar vehiclenumber, .....)虽然SSN和车辆号码是唯一的,但由于表格中有关于此人的信息,你可能希望SSN是PK而车辆编号是唯一的

另一方面,

你有一张表VehicleInfo (varchar owner_SSN, varchar vehiclenumber, .....)。即使在这种情况下,SSN和车辆编号也是唯一的,但由于在这种情况下表格是车辆,所以在这里你需要将车辆编号设为PK,SSN为unique

答案 3 :(得分:2)

基于这两个选项,我会选择第一个,因为它捕获了所有的设计要求:

  • 强制执行唯一性。
  • 性能。

第二个表设计也有效,但代价是维护两个索引的额外开销。

作为另一种选择,我实际上会进一步规范化UserClaims表,使其具有以下结构:

create table dbo.UserAccount
(
    UserAccountID   int not null primary key
)

create table dbo.Claim
(
    ClaimID     int             not null primary key
    , Type      nvarchar(150)   not null
    , Value     nvarchar(150)   not null
    , unique
    (
        Type, Value
    )
)

create table dbo.UserClaims 
(
    UserAccountID   int not null
    , ClaimID       int not null
    , primary key 
    (
        UserAccountID
        , ClaimID
    )
)

更新 - 要更具体地回答您的问题,如果您需要搜索给定值的所有用户声明,则可能需要使用第二个问题。

例如,向我展示声称超过$ 200,000的所有用户。

正如您所看到的,表格设计实际上取决于您的数据模型以及您要求的业务问题。

更新 - 鉴于提供的其他信息,您现在可以更清楚地了解您的问题。以下是我修改过的回答你问题的尝试。

它基本上归结为你从选项(2)得到的是你从选项(1)得到的东西?没什么我能看到的。

您绝对可以在选项(1)中的UserAccountID上创建外键:

alter table dbo.UserClaims
add constraint fk_UserClaims_UsersAccounts foreign key
(
    UserAccountID
)
references UsersAccounts
(
    UserAccountID
)

如果您按UserAccountID进行搜索,则两个选项中的聚簇主键都可以有效地获得结果。

如果要强制实施唯一性,则选项(1)中的主键和选项(2)中的唯一约束/索引都可以完成任务。

答案 4 :(得分:1)

PK NULL可以包含UNIQUE值。在这种情况下,您有NOT NULL个字段,但请记住所有一般情况下的这一重大差异。 第二个区别:每个表最多只有一个PK(并允许n个外键),而您可以根据需要设置UNIQUE个。{/ p>

示例: 您的表使用以下字段存储有关Sanitary Card(美国卫生系统)的数据:

  • 卡片代码
  • 用户社会安全号码(SSN)
  • 用户名/姓

在这种情况下,您的卡片代码可能是您系统的PK,因为您的上下文是卫生系统,但即使不在PK约束中,您也需要保持唯一的SSN。事实上,两个字段的PK(SSN和卡代码)允许相同的SSN用于不同的卡代码和相同的卡代码用于不同的SSN,同时我们需要保持两者唯一。 通常,我使用一个字段将上下文中的记录标识为PK,其他字段标识为UNIQUE。

因此,如果UserAccountID标识您的记录,则必须将字段设置为PK,将其他字段(VALUE,TYPE)设置为UNIQUE。提供一个表格示例,我们可以更具体。

答案 5 :(得分:1)

都不是。我将介绍一个新领域,特别是作为主键。我不确定你的业务逻辑,但我认为这三个看似合理的场景都排除了选项1:

  1. 我们针对错误的用户记录了索赔,需要更正! (需要更改UserAccountID)
  2. 我们使用错误的类型记录了声明,并且需要更正它(您需要更改类型)
  3. 声明的价值已更改(您需要更改价值金额)
  4. 这个非常合理的场景排除了选项2:

    1. 用户已提出第二项索赔。
    2. 如果这些数据项中的任何都可能发生变化,那么这意味着它们不适合用作PK。相反,只需添加一个新的自动递增整数字段作为ClaimID。它占用了更多的空间(一个小的缺点),但好处是你的自然数据值绝不会因为成为主键的一部分而受到人为限制。

      另外,我不会在这三个字段上添加唯一性约束;出于类似的原因,您的自然数据值可能不是唯一的。例如,如果用户输入相同类型的第二个声明,则表示相同的值(可能在不同的日期)。