【问题标题】:SQL Server 2008: What to set FK as PK?SQL Server 2008:如何将 FK 设置为 PK?
【发布时间】:2011-05-29 15:28:46
【问题描述】:

我正在整理自己的数据库,从我看到的示例中,外键也可以设置为主键。

我正在创建我的表格,以便我所有的 FK 也是 PK。这是错的吗? FK什么时候应该是PK?一定要PK吗?

主键在他们自己的表中是有意义的......作为 Id 和 Identity。但是当使用Id是另一个表时,它是否也必须是一个PK?

【问题讨论】:

    标签: sql-server sql-server-2008 database-design foreign-keys primary-key


    【解决方案1】:

    仅当您尝试创建 1 to 1 或 1 to zero/1 映射时,外键才应该是主键。

    例子:

    我有一个 Person 表、一个 Employee 表和一个 Contractor 表。所有员工都是人,所有承包商都是人,每个人要么是员工要么是承包商

    基本上你会得到这样的结果。


    为了响应您的人员有多个地址,您应该创建一个关联表。这是一张图表。

    您现在可以看到,每个人都可以有多个地址,并且由于每个员工都是一个人,因此每个员工都可以有多个地址。承包商也是如此。


    已编辑:这是来自 SQL Server 的更改脚本

    BEGIN TRANSACTION
    SET QUOTED_IDENTIFIER ON
    SET ARITHABORT ON
    SET NUMERIC_ROUNDABORT OFF
    SET CONCAT_NULL_YIELDS_NULL ON
    SET ANSI_NULLS ON
    SET ANSI_PADDING ON
    SET ANSI_WARNINGS ON
    COMMIT
    BEGIN TRANSACTION
    GO
    CREATE TABLE dbo.Address
        (
        AddressId bigint NOT NULL,
        Address nvarchar(50) NULL,
        City nvarchar(50) NULL,
        State nvarchar(50) NULL
        )  ON [PRIMARY]
    GO
    ALTER TABLE dbo.Address ADD CONSTRAINT
        PK_Address PRIMARY KEY CLUSTERED 
        (
        AddressId
        ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    
    GO
    ALTER TABLE dbo.Address SET (LOCK_ESCALATION = TABLE)
    GO
    COMMIT
    BEGIN TRANSACTION
    GO
    CREATE TABLE dbo.Person
        (
        PersonId bigint NOT NULL,
        Name nvarchar(50) NULL
        )  ON [PRIMARY]
    GO
    ALTER TABLE dbo.Person ADD CONSTRAINT
        PK_Person PRIMARY KEY CLUSTERED 
        (
        PersonId
        ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    
    GO
    ALTER TABLE dbo.Person SET (LOCK_ESCALATION = TABLE)
    GO
    COMMIT
    BEGIN TRANSACTION
    GO
    CREATE TABLE dbo.PersonAddress
        (
        PersonId bigint NOT NULL,
        AddressId bigint NOT NULL
        )  ON [PRIMARY]
    GO
    ALTER TABLE dbo.PersonAddress ADD CONSTRAINT
        PK_PersonAddress PRIMARY KEY CLUSTERED 
        (
        PersonId,
        AddressId
        ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    
    GO
    ALTER TABLE dbo.PersonAddress ADD CONSTRAINT
        FK_PersonAddress_Person FOREIGN KEY
        (
        PersonId
        ) REFERENCES dbo.Person
        (
        PersonId
        ) ON UPDATE  NO ACTION 
         ON DELETE  NO ACTION 
    
    GO
    ALTER TABLE dbo.PersonAddress ADD CONSTRAINT
        FK_PersonAddress_Address FOREIGN KEY
        (
        AddressId
        ) REFERENCES dbo.Address
        (
        AddressId
        ) ON UPDATE  NO ACTION 
         ON DELETE  NO ACTION 
    
    GO
    ALTER TABLE dbo.PersonAddress SET (LOCK_ESCALATION = TABLE)
    GO
    COMMIT
    BEGIN TRANSACTION
    GO
    CREATE TABLE dbo.Employee
        (
        EmployeeId bigint NOT NULL,
        EmployeeNumber nvarchar(50) NULL
        )  ON [PRIMARY]
    GO
    ALTER TABLE dbo.Employee ADD CONSTRAINT
        PK_Employee PRIMARY KEY CLUSTERED 
        (
        EmployeeId
        ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    
    GO
    ALTER TABLE dbo.Employee ADD CONSTRAINT
        FK_Employee_Person FOREIGN KEY
        (
        EmployeeId
        ) REFERENCES dbo.Person
        (
        PersonId
        ) ON UPDATE  NO ACTION 
         ON DELETE  NO ACTION 
    
    GO
    ALTER TABLE dbo.Employee SET (LOCK_ESCALATION = TABLE)
    GO
    COMMIT
    BEGIN TRANSACTION
    GO
    CREATE TABLE dbo.Contractor
        (
        ContractorId bigint NOT NULL,
        ContractorNumber nvarchar(50) NULL
        )  ON [PRIMARY]
    GO
    ALTER TABLE dbo.Contractor ADD CONSTRAINT
        PK_Contractor PRIMARY KEY CLUSTERED 
        (
        ContractorId
        ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    
    GO
    ALTER TABLE dbo.Contractor ADD CONSTRAINT
        FK_Contractor_Person FOREIGN KEY
        (
        ContractorId
        ) REFERENCES dbo.Person
        (
        PersonId
        ) ON UPDATE  NO ACTION 
         ON DELETE  NO ACTION 
    
    GO
    ALTER TABLE dbo.Contractor SET (LOCK_ESCALATION = TABLE)
    GO
    COMMIT
    

    【讨论】:

    • 例如,如果有一个用户表和一个地址表。 UserAddress 表是 1 对 1 的,对吧?那就是FK也是PK的时候?
    • 假设用户只有一个地址
    • 好吧,在这种情况下,用户有很多地址……这就是连接表的重点。我假设这意味着我应该放弃PK?只是两个 FK?
    • @John,在你上面的例子中......“ContractorId”实际上是“PersonId”? ...只是在表中使用它自己的名字?接触器表没有自己的 PK Id?
    • @dColumbus .. 事实上,Contractor 确实有自己的主键,由于 Person.PersonID 和 Contractor.ContractorID 之间的外键,它只是代表人员表中的相同值,创建一个 1 到 0 /1 Person 和 Contractor 之间的映射。
    【解决方案2】:

    只有一种情况需要 FK 也是 PK。

    当该表表示 PK 表中事物的子类或子集时,例如,SalriedEmployees 表与Employees 表有一个FK...

    【讨论】:

      【解决方案3】:

      FK 是指向另一个表的 PK 的字段。就是这样。
      链接到自身的表可能包含指向其自身 PK 的 FK。
      也是 FK 的 PK 只能发生在一对一关系的子表中。

      【讨论】:

      • 是的,但我看到表引用 FK 也是 PK。我在问什么...
      【解决方案4】:

      如果两个表具有一对一的关系并且添加了第二个表,因为第一个表太宽并且这些项目在大多数查询中并不总是需要,则 FK 也应该是 PK。

      如果您有一对多关系或多对多关系,它根本不起作用。

      FK 通常不是 PK。如果我有一个人表和一个相关的地址表,如果我将 PK 和 FK 设为相同的东西,那么我只能存储一个地址,但大多数地址表允许同一个人或组织的多个地址。在这种情况下,您会将 AddressID 作为 PK,将 person_id 作为 person 表的 FK。这是最常见的 PK/FK 场景。

      【讨论】:

        【解决方案5】:

        给定列既是 PK 又是 FK 的一种情况是 gen-spec 设计模式的关系模型。在其他响应之一中,“员工”是“人员”的专业化。员工表中的 PK 引用了人员表中的 PK。因此,专业表中的 PK 也是一个 FK。

        这允许创建一个视图,将员工和人员连接起来,以在一个视图中提供有关员工的所有数据,无论该数据是员工特有的(如“雇用日期”)还是所有人共有的数据,无论是不是他们是员工(例如“出生日期”)。

        让每个 PK 都成为 FK 并不是一个好习惯。 FK 应该反映数据的逻辑结构。如果逻辑模型不合逻辑,那么您就会遇到麻烦。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2017-04-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多