外键复合主键问题

时间:2021-07-27 08:52:16

标签: sql sql-server foreign-keys primary-key

我收到此错误:

<块引用>

引用表“User”中没有匹配的主键或候选键。

我检查了其他类似的问题,但不同的答案对我没有帮助。

我的主键设置错了吗?
我必须只引用 ShoppingCart 表中的一个主键吗?

enter image description here

CREATE TABLE [User]
(
    EmailAddress NVARCHAR(320),
    UserId INT IDENTITY(1,1),
    UserPassword VARCHAR(16),
    FirstName VARCHAR(256) NOT NULL,
    LastName VARCHAR(256) NOT NULL,
    MobileNumber BIGINT,
    PRIMARY KEY (EmailAddress, UserId)
)

CREATE TABLE [ShoppingCart]
(
    OrderId INT PRIMARY KEY IDENTITY(1,1),
    UserId INT FOREIGN KEY REFERENCES [User](UserId), //-- error here
    CreatedDate NVARCHAR(40)
)

非常感谢所有在下面回答我的用户。

1 个答案:

答案 0 :(得分:2)

正如我在评论中提到的,问题在于您正在尝试创建一个 FOREIGN KEY 来引用您的表 PRIMARY KEY 中的 UNIQUE CONSTRAINT/UserUserIDonly UserID 组成;不存在这样的约束,因此 FOREIGN KEY 的创建失败。

要从字面上解决这个问题,您需要将 EmailAddress 列添加到 ShoppingCart 表和 FOREIGN KEY 的定义:

CREATE TABLE dbo.[User]
(
    EmailAddress NVARCHAR(320),
    UserId INT IDENTITY(1,1),
    UserPassword VARCHAR(16),
    FirstName VARCHAR(256) NOT NULL,
    LastName VARCHAR(256) NOT NULL,
    MobileNumber varchar(20), --Numerical data types are a poor choice for a Phone Number
    PRIMARY KEY (EmailAddress, UserId)
)

CREATE TABLE dbo.[ShoppingCart]
(
    OrderId INT PRIMARY KEY IDENTITY(1,1),
    UserId INT,
    EmailAddress nvarchar(320),
    CreatedDate datetime2(0), --(n)varchar is not a "one size fits all" data type.
    FOREIGN KEY (EmailAddress,UserID) REFERENCES [User](EmailAddress,UserId)
)

事实上,这是一个坏主意,因为如果有人更改了他们的电子邮件地址,您认为会发生什么?好吧,让我们试试。首先,让我们创建一些示例数据:

INSERT INTO dbo.[User] (EmailAddress, FirstName, LastName)
VALUES(N'Jane@bloggs.com','Jane','Bloggs');

INSERT INTO dbo.ShoppingCart (UserId, EmailAddress, CreatedDate)
VALUES(1, N'Jane@bloggs', GETDATE());

现在假设 Jane 更改了她的电子邮件地址,因此我们尝试UPDATE 表格:

UPDATE dbo.[User]
SET EmailAddress = N'Jane.Bloggs@email.com'
WHERE UserId = 1;
<块引用>

INSERT 语句与 FOREIGN KEY 约束“FK__ShoppingCart__36DDA17A”冲突。数据库“Sandbox”,表“dbo.User”发生冲突。

这是因为表EmailAddressShoppingCart的值仍然是N'Jane@bloggs.com',所以无法更新该值。显然,您也不能UPDATE dbo.ShopppingCart 中的值,因为您会得到相同的错误:

UPDATE dbo.[ShoppingCart]
SET EmailAddress = N'Jane.Bloggs@email.com'
WHERE UserId = 1;
<块引用>

INSERT 语句与 FOREIGN KEY 约束“FK__ShoppingCart__415B2FED”冲突。数据库“Sandbox”,表“dbo.User”发生冲突。

那你会怎么做呢?好吧,我假设电子邮件地址在应用程序中是唯一的,不是用户。目前,您可以有 100 个用户的 UserID1,并且如果他们都有不同的电子邮件地址,它们将是有效的主键。很可能您真正想要的是,基于您不希望表 EmailAddress 中的 ShoppingCardUserID 是主键,并且电子邮件地址具有单独的唯一约束。这将为您提供以下内容:

CREATE TABLE dbo.[User] -- I recommend against the name USER, it's a reserved keyword
(
    EmailAddress NVARCHAR(320),
    UserId INT IDENTITY(1,1),
    UserPassword VARCHAR(16),
    FirstName VARCHAR(256) NOT NULL,
    LastName VARCHAR(256) NOT NULL,
    MobileNumber varchar(20), --Numerical data types are a poor choice for a Phone Number
);

ALTER TABLE dbo.[User] ADD CONSTRAINT PK_User PRIMARY KEY (UserId);
ALTER TABLE dbo.[User] ADD CONSTRAINT UQ_UserEmail UNIQUE (EmailAddress);
GO
CREATE TABLE dbo.[ShoppingCart]
(
    OrderId INT IDENTITY(1,1),
    UserId INT,
    CreatedDate datetime2(0), --(n)varchar is not a "one size fits all" data type.
)
ALTER TABLE dbo.[ShoppingCart] ADD CONSTRAINT PK_ShoppingCart PRIMARY KEY (OrderId);
ALTER TABLE dbo.[ShoppingCart] ADD CONSTRAINT FK_ShoppingCart_User
    FOREIGN KEY (UserId)
    REFERENCES dbo.[User] (UserID);

我还为您的约束提供了明确的名称,这是您真的应该养成的习惯。

现在您可以毫无问题地INSERT 示例数据和UPDATE 电子邮件地址:

INSERT INTO dbo.[User] (EmailAddress, FirstName, LastName)
VALUES(N'Jane@bloggs.com','Jane','Bloggs');

INSERT INTO dbo.ShoppingCart (UserId, CreatedDate)
VALUES(1,GETDATE());
GO

UPDATE dbo.[User]
SET EmailAddress = N'Jane.Bloggs@email.com'
WHERE UserId = 1;

如果您尝试添加具有相同电子邮件地址的其他用户,则会出现错误:

INSERT INTO dbo.[User] (EmailAddress, FirstName, LastName)
VALUES(N'Jane.Bloggs@email.com','Jane','Bloggs');
<块引用>

违反 UNIQUE KEY 约束“UQ_UserEmail”。无法在对象“dbo.User”中插入重复键。重复的键值为 (Jane.Bloggs@email.com)。

相关问题