【问题标题】:Is it possible to set a non primary key as foreign key in another table?是否可以在另一个表中将非主键设置为外键?
【发布时间】:2017-01-21 11:02:14
【问题描述】:

更具体地说,我想将AspNetUsers 表中的Email 列设置为另一个表中的外键。

我希望能很好地解释我的问题。

【问题讨论】:

    标签: c# sql database entity-framework asp.net-web-api


    【解决方案1】:

    是和否

    SQL Server,具体来说:

    如果您只是询问 SQL Server(并且不是实体框架),那么我 quoteth Microsoft's documentation:

    外键约束不必只链接到另一个表中的主键约束。也可以定义外键来引用另一个表中 UNIQUE 约束的列。

    因此直接回答您提出的问题:

    • 是否可以将非主键设置为另一个表中的外键?
      • 如果它们是“普通”列,则否,否则 SQL Server 无法知道这些值将唯一标识每一行。
      • 但是,如果这些列位于 UNIQUE CONSTRAINT 中或应用了未过滤 UNIQUE INDEX,那么是的,您可以。李>
      • 这需要 SQL Server 2005 或更高版本。 (我可能错了,但我不相信 SQL Server 2000 支持引用 UNIQUE CONSTRAINT 列的 FOREIGN KEY 约束列。

    There are subtle differences between UNIQUE CONSTRAINT and UNIQUE INDEX,虽然主要区别是表明意图 - 所以当你使用一个或多个列作为辅助时,你应该更喜欢UNIQUE CONSTRAINT 而不是UNIQUE INDEX键。

    实体框架

    • 实体框架 6

      • 我承认我不确定。我记得必须真正与 EF6 作斗争,并使用 VIEWs 之类的变通方法来使 EF6 使用对 UNIQUE KEY 约束的 FK 引用很好地发挥作用,但一些报告表明,现代版本的 EF6 确实 原生支持这一点.
    • 实体框架核心

      • 是的。使用HasPrincipal()HasForeignKey() 来定义TPrincipal 上的辅助键和指向该辅助键的TDependant 上的外键。

    至于你的场景,具体来说:dbo.AspNetUsers:

    假设您想使用Email 列来唯一标识用户,那么我建议您不要这样做:一般来说,使用可变数据(即可以更改的数据)作为键。密钥必须是唯一的,理想情况下应该是不可变的 - 虽然电子邮件地址在某些情况下可以是唯一的(例如,您的系统不允许多个用户共享同一个电子邮件地址),但它们不是不可变的(用户 可以并且确实更改电子邮件地址)。

    另一个警告:为了使用 UNIQUE CONSTRAINTUNIQUE INDEX 作为外键目标,那些列不能被过滤或有NULLs - 但@987654342 @ 是 nvarchar(256) NULL,因此您需要确保所有用户都设置了不同的电子邮件地址首先

    话虽如此,这将起作用:

    -- 1. Change `Email` from `NULL` to a `NOT NULL` column, as `UNIQUE CONSTRAINT` columns cannot contain duplicates or nulls:
    ALTER TABLE dbo.AspNetUsers
        ALTER COLUMN [Email] nvarchar(256) NOT NULL;
    
    -- 2. Create a UNIQUE CONSTRAINT over the `Email` column, so SQL Server knows every row has a distinct value in that column:
    ALTER TABLE dbo.AspNetUsers
        ADD CONSTRAINT UQ_Email UNIQUE ( Email );
    
    -- 3. Create a new table with a FOREIGN KEY in the other table:
    CREATE TABLE dbo.OtherTable (
        Etc       nvarchar(50) NOT NULL,
        UserEmail nvarchar(256) NULL,
        
        CONSTRAINT FK_Users_by_email FOREIGN KEY ( UserEmail ) REFERENCES dbo.AspNetUsers ( [Email] )
    );
    

    【讨论】:

    • @GurV 我假设因为 OP 提到了AspNetUsers,他们正在使用 SQL Server——尽管我很欣赏 ASP.NET Identity 也可用于其他数据库系统。
    • 它是 EF 限制而不是 SQL Server。例如,EF Core 完全支持备用(唯一)键和引用它们的 FK。
    • 我能够为非 pk 列创建外键。没有问题。
    • @DeniseMichelledelBando 除了应该不可能的UNIQUE CONSTRAINTUNIQUE INDEX 列。你能在 SqlFiddle 中重现它并分享你的结果吗?
    【解决方案2】:

    是的,您可以使用 fluent api 在 asp.net 核心中通过主键创建外键。您可以从here 的 microsoft doc 中查看示例

    【讨论】:

    • 您的答案是指在实体框架中使用或定义 unenforced 外键约束,而不是直接在 SQL 中 - 所以虽然 EF 会尊重虚构的约束,但其他程序和原始SQL 不会 - 并且假设 EF 中的内置 DDL 代码生成不会抱怨使用当前后备存储不支持的功能。
    • 我首先将主列设为唯一,然后添加外键,它就像一个魅力。谢谢您的帮助。 +1
    【解决方案3】:

    我能够为非 pk 列创建外键。没问题。

    CREATE TABLE [dbo].[t1](
    [id] [int] NOT NULL,
    [fldKey] [int] NULL,
    CONSTRAINT [PK_t1] PRIMARY KEY CLUSTERED 
    (
    [id] 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
    
    CREATE TABLE [dbo].[t2](
    [id] [int] NOT NULL,
    [t1] [int] NULL,
    CONSTRAINT [PK_t2] PRIMARY KEY CLUSTERED 
    (
    [id] 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].[t2]  WITH CHECK ADD  CONSTRAINT [FK_t2_t1] FOREIGN 
    KEY([t1])
    REFERENCES [dbo].[t1] ([fldKey])
    GO
    ALTER TABLE [dbo].[t2] CHECK CONSTRAINT [FK_t2_t1]
    GO
    

    【讨论】:

    • 当我运行此 SQL 时,我收到以下错误:“引用表 'dbo.t1' 中没有与外键 'FK_t2_t1' 中的引用列列表匹配的主键或候选键。” , "无法创建约束或索引。请参阅以前的错误。"。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-23
    • 2012-03-29
    • 2011-03-05
    • 2014-08-27
    相关资源
    最近更新 更多