在SQL Server中引用由两列组成的主键的外键。

55

这个问题与这个类似,但是针对的是SQL Server 2005:

我在我的数据库中有2个表:

--'#' denotes the primary key
[Libraries]
#ID   #Application  Name
 1     MyApp        Title 1
 2     MyApp        Title 2


[Content]
#ID   Application  LibraryID  Content
 10    MyApp       1          xxx
 11    MyApp       1          yyy

(数据库显然更加复杂,拥有这种双重键是有意义的)

每个库都由其唯一ID和应用程序名称标识。我试图确保每个内容都正确引用了现有库。

在使用向导创建约束时:

Primary key table            Foreign key table
[Libraries]                  [Content]
ID                  --->     LibraryID
Application         --->     Application

我遇到了以下错误:

表 'Libraries' 的列没有匹配现有的主键或唯一约束条件

您知道这是怎么回事吗?如果可能的话,能否使用SQL Server解决?(我无法修改[Library]表)

非常感谢您的帮助!


这个解决方案相当笨拙。对于你的任务来说,只需要在应用程序中使用简单的外键引用库就足够了。你从哪里得到需要两列的想法? - Vincent
1
@Vincent 12年前的我可能有充分的理由,但是如今我绝对不会选择双列PK :) - Luk
5个回答

81

当然可以创建一个外键关系到由多个列组成的主键。你没有展示你正在使用的语句来尝试创建这个关系 - 它应该是类似于:

ALTER TABLE dbo.Content
   ADD CONSTRAINT FK_Content_Libraries
   FOREIGN KEY(LibraryID, Application)
   REFERENCES dbo.Libraries(ID, Application)

这是您正在使用的吗?如果(ID, Application)确实是dbo.Libraries上的主键,那么此语句一定会起作用。

Luk:只是为了确认-您能否在数据库中运行此语句并汇报输出结果?

SELECT
    tc.TABLE_NAME,
    tc.CONSTRAINT_NAME, 
    ccu.COLUMN_NAME
FROM 
    INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc
INNER JOIN 
    INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE ccu 
      ON ccu.TABLE_NAME = tc.TABLE_NAME AND ccu.CONSTRAINT_NAME = tc.CONSTRAINT_NAME
WHERE
    tc.TABLE_NAME IN ('Libraries', 'Content')

我当时直接使用了UI,但是使用你的语法会引发以下错误:"在外键中引用的表'dbo.Libraries'中没有与引用列列表匹配的主键或候选键..." - Luk
那么,在这种情况下,(Id,Application)对于Libraries表不是主键。您只能引用父表上的主键或作为唯一索引一部分的列(或一组列)。请检查您的“Libraries”表! - marc_s
这也是我的第一个想法,但 SQL 脚本指定了 CREATE TABLE [Libraries] (... CONSTRAINT [PK_sf_Libraries] PRIMARY KEY CLUSTERED ( [Application] ASC, [ID] ASC) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]...(抱歉在此倾倒所有的 SQL)。 - Luk
1
@Luk:我添加了一条语句,你可以在你的数据库上运行它 - 只是为了检查一下..... - marc_s
2
有人考虑过“威尔·拉塞尔”在他“2014年3月18日14:43”下面的答案中提出的理论吗?我知道Luk说他在“新数据库”中让它工作,但是如果他“使用UI”(而不是修改自动生成的脚本)正如他至少一次承认为至少他最初的FK添加尝试,那么他也很可能在“新数据库”中这样做了,因此在“新数据库”中,他意外地使列的顺序保持一致(在创建表中的PK定义和在Alter Table中的FK定义中)。 - Tom
显示剩余2条评论

14
请注意,字段必须按照相同的顺序排列。如果您引用的主键被指定为(应用程序、ID),则您的外键必须引用(应用程序、ID)而不是(ID、应用程序),因为它们被视为两个不同的键。

1
我怀疑这也是问题所在,特别是因为他在主键定义中首先列出了“Application”(“PRIMARY KEY CLUSTERED ([Application] ASC, [ID] ASC)”),并在他的“Jul 5'10 at 15:07”评论中列出了“应用程序”,但在他的OP示例中两者都首先列出了“ID”。我很惊讶在你之前的4年8个月中没有其他人(包括500(粉红色到嘴唇)THOUSAND更多Rep'ed“marc_s”)指出这一点。 - Tom
天哪,我花了好几个小时才想明白,结果这就是答案。谢谢!!! - codingguy3000

6
关键在于“列的顺序应该相同”
例子:
create Table A (
    A_ID char(3) primary key,
    A_name char(10) primary key,
    A_desc desc char(50)
)

create Table B (
    B_ID char(3) primary key,
    B_A_ID char(3),
    B_A_Name char(10),
    constraint [Fk_B_01] foreign key (B_A_ID,B_A_Name) references A(A_ID,A_Name)
)

表A的列顺序应为--> A_ID 然后是 A_Name;定义外键时应遵循相同的顺序。

1

Content表可能有多个重复的Application值,这些值无法映射到Libraries。是否可以从Libraries主键索引中删除Application列,并将其作为唯一键索引添加?


是的,也许Application会有重复,但这并不需要是唯一的。 (LibraryID,Application)对甚至可以有多个值 - 这不是Content表上的PK - 而是Libraries表上的PK! - marc_s
好的,那么当您从内容中获得(LibraryID,Application),但它与图书馆上的(ID,Application)不对应时会发生什么?目前为止,没有强制执行的约束。如果OP不想更改当前的结构,我建议他尝试在(LibraryID,Application)上创建唯一键索引,并查看是否可以成功创建。然后再尝试创建关系。 - Adrian Godong
很遗憾,我完全不能触碰Libraries。这两个表甚至应该有一个1-1的关系,因为我不能向Libraries添加新列。你是对的,在那里需要一个唯一索引(但那并没有解决问题)。 - Luk
我是不是错了以为只能在外键中引用列? - Luk
Luk:你可以在一个外键中引用两个列。你是否检查过Contents中的所有行是否都可以映射到Libraries?(一个简单的LEFT JOIN将确定您是否可以这样做) - Adrian Godong
内容目前为空,IDLibraryID 都是 uniqueidentifier NOT NULL,而 Application 在两侧都是 nvarchar(50) NOT NULL。这有点奇怪,我可能会稍后以更清醒的头脑回来。 - Luk

0

我曾经遇到过同样的问题,现在我认为我有了解决方案。

如果你的表 Library 中的字段 Application 有一个外键引用另一个表中的字段(我猜应该是名为 Application 的表),那么你的表 Library 中的字段 Application 也必须有一个外键引用表 Application

之后你就可以进行组合外键操作了。

请原谅我的英语不好,如果我说错了什么也请见谅。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接