2012-12-14 95 views
3

请看看下面的数据库设计:SQL - NULL外键

create table Person (id int identity, InvoiceID int not null) 
create table Invoice (id int identity, date datetime) 

目前,所有的人都有一个invoiceIDInvoiceIDnot null

我想扩展数据库,以便某个人没有Invoice。最初的开发人员恨零,从不使用它们。我想要保持一致,所以我想知道是否有其他模式可用于扩展数据库以满足此要求。如何在不使用空值的情况下接近?

请注意,上面的两个表格仅用于说明目的。它们不是实际的表格。

+5

'NULL'具体用于*当你没有值的情况下 - 所以在这种情况下,我认为使用'NULL'是最合适的方法。 –

+1

虽然我通常会同意保持一致性,但我完全不同意dbs中的'NULL hating'。这绝对不是您想要进一步传播的想法。这很愚蠢,而且根本没有建设性。 –

+0

每个人只能有一张发票吗?您拥有它的方式,似乎每张发票都可以有多个人,但是没有其他方式。 –

回答

3

常见模式是将该关联存储在单独的表中。

人:身份证 发票:身份证 副教授:为person_id,assoc_id

然后一个人如果没有发票,你根本就没有一行。这种方法还允许一个人拥有多个可能有意义的发票编号。

+3

ps - 我不认为“原始开发人员不喜欢空值,所以我不会使用它们”是避免空值的一个很好的理由。 –

+0

与此答案的警告是,它确实类似于多对多的关系。它不必这样使用!如果你真的不能有NULL,这是一个完全可以接受的答案。但我会考虑切换到NULL。 –

+2

@EliGassert我实际上已经从NULL-FK移走了*! – 2012-12-14 19:55:44

5

NULL是一般数据库和编程中非常重要的功能。它与零或任何其他值显着不同。它通常用于表示缺乏价值(虽然它也可能意味着未知价值,但这不太用作解释)。如果某些人没有发票,那么您应该真正允许NULL,因为这与您所需的Schema匹配。

+2

一些着名的实践者认为,NULL是一个非常糟糕的bug,如果你在设计中使用它 - 例如克里斯日期。这是否意味着价值或未知的缺失,以及它如何影响选择和分组? – Mark

+2

NULL是一个重要的功能 - 好吧,让我们假设这是真的(因为我不认为这是问题的核心)。然而,这个问题不是关于“一般空”(例如表示“缺失数据”),而是关于“空*外键*”。我认为,尽管它可能会使模型“稍微复杂一些“,禁止零FK实际上使生活更加一致。它还避免了SQL Server中的多个NULL值不被视为不同的“问题”(例如,在唯一索引中)。 – 2012-12-14 19:58:37

+0

@Mark我提到了这两点 - 缺席与未知/缺失 - 我仍然坚持我的答案。我认为重要的是要有最好的反映他想要完成的事情。我意识到有些人说这是一个糟糕的设计;我只是倾向于不同意,我坚持我的回答。 –

1

您需要将发票/人员关系移至另一个表。 你最终

create table Person (id int person_identity) 
create table PersonInvoice (id int person_id, InvoiceID int not null) 
create table Invoice (id int identity, date datetime) 

你需要这个对于一些数据库,以便能够在InvoiceId是一个外键一些不允许在一个外键空值。

如果一个人只能拥有一张发票,那么PersonInvoice可以对person_id和两个列一起具有唯一约束。您还可以通过向invoiceID字段添加唯一约束来强制让一个人拥有发票。

+0

连接表格式只有在您打算在一张发票上包含多个人员时才有效。如果发票只能有一个人,则不需要单独的表。从发票到人只需一个外键。 –

+2

@PaulSasik在这种情况下声明一个联接表是“唯一有效的”是不正确的。虽然连接表*通常*表示M-M,但它也可以表示基于列的基数的1-M和1-1关系。在这种情况下,PersonInvoice将具有唯一(person_ID)和唯一(invoice_ID)。 (阅读:每个人可以有一张发票,一张发票最多可以属于一个人。) – 2012-12-14 20:03:13

3

在避免空值的情况下表示可选关系的唯一方法是使用另一个表,如其他一些答案所建议的。然后,对于给定的人没有行表示该人没有发票。通过使PERSON_ID是主要或唯一键此表和Person表1之间的关系:

CREATE TABLE PersonInvoice (
    person_id INT NOT NULL PRIMARY KEY, 
    invoice_id INT NOT NULL, 
    FOREIGN KEY (person_id) REFERENCES Person(id), 
    FOREIGN KEY (invoice_id) REFERENCES Invoice(id) 
); 

如果你要允许每个人拥有多张发票,你可以声明的主键可以强制执行1作为一对列。

但是这种解决方案是为了满足您的要求,以避免NULL。这是人为的要求。 NULL在数据模型中具有合法的位置。

一些像Chris Date这样的关系数据库理论家避开了NULL,解释了NULL的存在会导致关系逻辑中一些令人不安的逻辑异常。对于这个阵营来说,上面没有一行是代表缺失数据的更好方法。

但其他理论家,包括E.F。撰写关于关系理论的开创性论文的Codd承认占位符的重要性,意味着“不知道”或“不适用”。 Codd甚至在1990年的书中提出,SQL需要两个占位符,一个用于“缺少但适用”(即未知),另一个用于“缺失但不适用”。

对我来说,我们在某些方面使用NULL时看到的异常就像我们在算术中看到的未定义结果,除以零。解决方案是:不要那样做

但是当然我们不应该使用任何非NULL值,如0或''(空字符串)来表示缺失的数据。同样,我们不应该使用NULL,就好像它是一个普通的标量值。

我在我的书SQL Antipatterns: Avoiding the Pitfalls of Database Programming的一篇名为“对未知的恐惧”一章中写了更多关于NULL的内容。

+1

如果您还没有阅读比尔的书,我强烈建议您获取一份并阅读它。没有人熟悉本书的概念,没有人应该研究数据库设计和查询。 – HLGEM

+0

谢谢@HLGEM!顺便说一句,抱歉我的书一直在卖,所以只有在我给出了真正的答案之后,或者与一些免费资源联系起来的时候,我才会这样做。 –