2013-01-31 41 views
1

我有一个数据库结构,其包括如下表:使用外键,我可以在复合主键中引用固定值吗?

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 
) 

很显然,我可以只添加一个可空“ExtraDetails”字段中PaymentProvidersForEntities表,但我没有发现,很优雅一样会有几种不同类型的支付提供者,每个需要不同类型的额外的细节。有没有一种优雅的方式来做我想做的事?如果不是,那么实现同样目标的更好方法是什么?

+0

级联删除和更新可能会给经验丰富的DBA带来问题。我会建议你删除它们并手动执行需要级联的更新/删除 – gh9

+1

你能提供参考吗?我将自己形容为“经验丰富”的DBA,目前我对他们没有任何问题......但我有兴趣了解他们导致他人的问题。 –

回答

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。扩展到支持两个,三个或四个支付提供商而不是一个支付提供商也更简单。

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

+0

这看起来像是一个更好的解决方案。谢谢!是的,这个想法本来就是有不同名称的表 - 比如PayPalExtraDetails或GoogleCheckoutExtraDetails。我已经更新了这个问题来反映这一点。 –

+0

此外,它还不如您可以使用我在问题中提出的语法那么优雅。我想知道是否会有更好的解决方案? –

-1

如果你的第二个表的主键是一个代理键,并且你对你想要的组合键的一部分做了一个外键。

例如,pseudosql


酒吧INT的PrimaryKey
foobar的INT的PrimaryKey

测试
命名为testFoo INT的PrimaryKey
foobar的INT foriengkey

只要确保你只放您想要的测试项目。如果你真的想成为安全的,你可以把一个约束上foobar确保它不仅具有价值你想

+0

我不知道这是否有效,但我可能会误解。你能澄清你的意思吗?也许真正的SQL? –

+1

*“如果第二个表的主键是代理键,并且为组合键的一部分创建了一个外键,那么将为您执行该操作。”*首先,在任何符合条件的SQL dbms中与SQL标准。外键引用的目标在其列上必须具有主键约束或唯一约束。其次,即使它有效,也不能解决OP的问题。“第二个”表格需要引用一个且唯一的一个支付提供商ID。 –

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); 
+0

似乎是一个很好的解决方案,谢谢! –

+0

是这个标准的sql吗?还是特定于某些数据库? – jambriz

+0

这是在Microsoft SQL Server上测试的。这可能不是标准。 –