使用外键,我可以在复合主键中引用固定值吗?

时间:2013-01-31 16:13:45

标签: sql sql-server sql-server-2008 tsql sql-server-2012

我有一个数据库结构,其中包括下表:

CREATE TABLE dbo.PaymentProvidersForEntities
(
    PaymentProviderId SMALLINT NOT NULL,
    EntityId BIGINT NOT NULL, 
    CONSTRAINT PK_PaymentProvidersForEntities 
        PRIMARY KEY (PaymentProviderId, EntityId), 
    CONSTRAINT FK_PaymentProvidersForEntities_PaymentProviders 
        FOREIGN KEY (PaymentProviderId) 
        REFERENCES PaymentProviders(PaymentProviderId) 
        ON DELETE CASCADE ON UPDATE CASCADE,
    CONSTRAINT FK_PaymentProvidersForEntities_Entities 
        FOREIGN KEY (EntityId) 
        REFERENCES Entities(EntityId) 
        ON DELETE CASCADE ON UPDATE CASCADE
)

显然,这是一个带有复合主键的简单多对多链接表。我想要另一个引用该表的表,但只提供一个PaymentProvider的数据(即PaymentProviderId =固定值)。类似的东西:

CREATE TABLE dbo.SpecificPaymentProviderExtraDetails
(
    EntityId BIGINT NOT NULL,
    ExtraDetails NVARCHAR(MAX) NOT NULL,
    CONSTRAINT PK_PaymentProviderExtraDetails 
        PRIMARY KEY (EntityId), 
    CONSTRAINT FK_PaymentProviderExtraDetails_PaymentProvidersForEntities 
        FOREIGN KEY (EntityId, 1) 
        REFERENCES PaymentProvidersForEntities(EntityId, PaymentProviderId) 
        ON DELETE CASCADE ON UPDATE CASCADE
)

显然我可以在PaymentProvidersForEntities表中添加一个可以为空的'ExtraDetails'字段,但我发现它并不优雅,因为会有几种不同类型的支付提供商,每种提供商都需要不同类型的额外细节。做一个我想要的优雅方式吗?如果没有,那么实现相同目标的更好方法是什么?

3 个答案:

答案 0 :(得分:2)

最简单的方法是存储付款提供商ID,并使用CHECK()约束来确保它是您想要的。假设您希望支付提供商ID始终等于13。

CREATE TABLE dbo.PaymentProviderExtraDetails
(
    PaymentProviderID SMALLINT NOT NULL DEFAULT 13 CHECK(PaymentProviderID = 13),
    EntityId BIGINT NOT NULL,
    ExtraDetails NVARCHAR(MAX) NOT NULL,
    CONSTRAINT PK_PaymentProviderExtraDetails 
        PRIMARY KEY (PaymentProviderID, EntityId), 
    CONSTRAINT FK_PaymentProviderExtraDetails_PaymentProvidersForEntities 
        FOREIGN KEY (PaymentProviderID, EntityID) 
        REFERENCES PaymentProvidersForEntities(PaymentProviderId, EntityId) 
        ON DELETE CASCADE ON UPDATE CASCADE
);

我更喜欢这种持久化列的方法,因为这种方法遵循principle of least surprise。扩展到支持两个,三个或四个支付提供商而不是一个支持提供商也简单得多。

如果我是你,我会重新考虑表名。 可能应该以您为其记录数据的单一支付提供商命名。

答案 1 :(得分:1)

您可以将持久计算列添加到引用列列表中:

CREATE TABLE dbo.p(i1 INT, i2 INT, CONSTRAINT p_pk PRIMARY KEY CLUSTERED (i1,i2));
CREATE TABLE dbo.c(id INT PRIMARY KEY CLUSTERED,i1 INT, i2 AS 1 PERSISTED, CONSTRAINT c_fk FOREIGN KEY (i1,i2) REFERENCES dbo.p(i1,i2));
INSERT INTO dbo.p(i1,i2)VALUES(42,1);
INSERT INTO dbo.c(id,i1)VALUES(111,42);

答案 2 :(得分:-1)

如果您的第二个表的主键是代理键,并且您为所需的复合键部分创建了一个外键,那么它将为您完成。

例如,pseudosql

Foo
bar int primarykey
foob​​ar int primarykey

测试
testFoo int primarykey
foob​​ar int foriengkey

确保只放置测试中的项目。如果您真的想要安全,可以在foobar上设置约束,确保它只有您想要的值