在PostgreSQL中为不能为null的字段不指定NOT NULL有什么后果?


10

我有一个应用程序(数据存储在PostgreSQL中),其中表中的大多数字段始终不为null,但是这些表的架构并未强制执行此操作。例如看这个假表:

CREATE TABLE "tbl" (
    "id" serial,
    "name" varchar(40),
    "num" int,
    "time" timestamp
    PRIMARY KEY ("id"),
    UNIQUE ("id")
);

此外namenumtime没有明确提及的NOT NULL,在现实中是这样,因为执行发生在应用端。


我的感觉是应该对其进行更改,但相反的是,应用程序级别确保此处不会出现空值,并且没有其他人手动修改该表。

我的问题是:通过设置显式NOT NULL约束?

我们拥有一个良好的代码审查流程和一个相当不错的文档,因此,某些新人提交的东西可能会破坏此约束,这实际上不足以证明更改是正确的。

这不是我的决定,所以这正是我在寻找其他理由的原因。我认为,如果某些内容不能为null,并且数据库允许您指定某些内容不为null,则只需执行此操作即可。特别是如果更改非常简单。


1
有关空值和磁盘空间的注意事项,请参见以下答案:stackoverflow.com/questions/5008753/…简而言之,如果您的表有8列以上且至少有1个可空列,那么与每列相比,该表每行将需要更多字节定义不为null。
ypercubeᵀᴹ

1
@ypercubeᵀᴹ:确切地说,只有在行中有实际null值的情况下,才为每行添加null位图:stackoverflow.com/a/7654497/939860。因此,NOT NULL约束对存储大小没有任何直接影响。当然,在定义了所有列的情况下NOT NULL,不能以空位图开头。另一方面:对于没有实际值的列,如果您使用NULL而不是“空”或虚拟值,则存储大小通常会小得多,因为空位图相对要小得多(少数情况除外)。
Erwin Brandstetter,2016年

@ErwinBrandstetter那时我很糟糕,还不了解那部分。因此,对于没有空值的列,在存储上没有真正的区别,是否将它们定义为NULL或NOT NULL,对吗?索引存储空间也一样吗?
ypercubeᵀᴹ

5
“应用程序级别确保null值不会在此处出现”不,不是。它可以确保一个应用程序不插入空值。但是我有psql(例如),并且我可以在您的应用程序不知道的情况下有意和无意地插入null。
Mike Sherrill'Cat Recall'16

5
可以确保没有人手动修改表的唯一应用程序是dbms本身。
Mike Sherrill'Cat Recall'16

Answers:


9

当新程序员到来并必须针对该数据库编写应用程序时会发生什么?他们不知道x字段必须NOT NULL

另一个程序可能会假设所有字段x都NOT NULL用于执行计数,但是现在某些字段是NULL由于新程序造成的,从而导致不一致且难以跟踪错误。

恕我直言,最好总是在尽可能接近数据的地方(即在数据库中)实施数据完整性规则。这样,新的应用程序和/或程序员就不会弄乱您的数据。

程序员,应用程序,语言和框架随处可见。数据和数据库趋于持久。数据库是您针对不一致,潜在错误数据的最后一道防线。

最大利用你的数据库的完整性约束执法机制,甚至不惜牺牲性能为代价。产生正确的结果的慢行系统是无限优于快一个把事情错了!


1
IMHO it is always best to enforce data integrity rules as near to the data as possible这实际上与我所写的直觉相同。这正是我在寻找真正理由的原因。我们已经进行了代码审查并获得了很好的文档,因此,对于新开发人员不了解某些内容的担忧不足以证明更改是正确的。
萨尔瓦多·达利

4
代码审查和好的文档并不能保证您不会(编程或其他)错误。
ypercubeᵀᴹ

2
在陷入紧迫的期限之前,有多少人REAL PROGRAMMERS阅读了全部(或什至全部)文档?
Vérace

3
我曾经在一家对数据仓库持相同态度的银行进行过审查。在这种情况下-没有参照完整性。好吧,碰巧有40%的旧数据是垃圾数据,因为有人没有阅读文档并删除了查找表中的数据。您不信任具有数据完整性的代码审查和文档-您在数据库中将其明确声明。
TomTom

5

正如其他人在评论中已经引用的那样,添加NOT NULL到表规范中可以显着改善查询的性能(除了另一个答案中所述的非常好的方法学原因之外)。

原因是查询优化器知道列不能有NULL值,因此可以排除针对此类值的特殊测试,例如在NOT INvs. NOT EXISTS情况下。例如,您可以看到此blog,其中显示了不NOT NULL通过特定查询声明字段(当表始终包含非null值时)会增加500%的执行时间。显示了针对SQL Server的结果,但是在其他关系DBMS(例如您的关系数据库)中可能会出现类似的行为(更不用说您的数据库可以移植到其他系统的事实了)。您可以假定的一般规则是,当查询优化器可以使用更多信息时,可以生成更有效的访问计划。


谢谢。这是我一直在寻找的答案类型。
萨尔瓦多·达利

5
NOT NULL出于多种原因,应定义永远不包含NULL的列,对此不加任何参数。但是,有关SQL Server博客的链接不适用于Postgres,也不能证明您提到的任何性能影响。没有说没有,但是我很想看看实际的证据
Erwin Brandstetter,2016年

@ErwinBrandstetter,我对PostgreSQL优化器抱有很高的期望:(经过几次测试,在有和没有NOT NULL约束的情况下,我在PostgreSQL博客中提出的NOT IN查询中都没有发现显着差异。因此,我改变了答案,和我问你,如果你认为我应该完全删除。
伦佐

不,我不认为应该删除它。它有5票以上,没有反对票。
ypercubeᵀᴹ

not in可空列的语义是不同的,因此两者之间的计划一定有所不同吗?
马丁·史密斯

2

空间影响

空间的影响在这个职位由@Erwin Brandstetter修改谈起

简而言之,如果数据库包含以下内容,则您将保存一位totalColumns - 8舍入到最接近的字节(或MAXALIGN):

  1. 8列以上
  2. 表格上的所有列都是NOT NULL

性能影响

但是,在@Erwin Brandstetter在SE上发表的这篇文章中,他说

  1. “设置NOT NULL本身对性能没有影响。检查的几个周期-不相关。”
  2. “ ...实际上是使用NULL而不是伪值。根据数据类型,您可以节省大量磁盘空间和RAM,从而加快了所有速度。”

@Renzo有一个谈论性能影响的答案 - 我认为这都不适用于PostgreSQL。我找不到任何证实任何的作为是有关到PostgreSQL。保存的任何周期即使在最基本的查询中也无法量化。

CREATE TABLE foo (
  a int,
  b int NOT NULL,
  x float,
  y float NOT NULL
);

INSERT INTO foo ( a, b, x, y )
SELECT x, x, x, x
FROM generate_series(1,1E7) AS X(x);

EXPLAIN ANALYZE SELECT 1/a FROM foo;
EXPLAIN ANALYZE SELECT 1/b FROM foo;
EXPLAIN ANALYZE SELECT 1/x FROM foo;
EXPLAIN ANALYZE SELECT 1/y FROM foo;

另外,我进行了一些测试,以查看NULL索引是否更快,而我无法证实这一点。您可以在邮件列表中找到Scott Marlowe所提供的非常有用的线程,线程讨论了9.1中的查询计划程序,该程序可以在不同的WHERE子句上使用部分索引。我通过运行以下命令对此进行了测试

CREATE TABLE foo ( a int );
CREATE TABLE bar ( a int NOT NULL );
INSERT INTO foo
  SELECT null FROM generate_series(1,1e5) AS x
  UNION ALL
  SELECT 10
  UNION ALL
  SELECT null FROM generate_series(1,1e5) AS x
;
INSERT INTO bar
  SELECT 0 FROM generate_series(1,1e5) AS x
  UNION ALL
  SELECT 10
  UNION ALL
  SELECT 0 FROM generate_series(1,1e5) AS x
;

现在,我创建了索引,

CREATE INDEX foobar ON foo(a) WHERE a IS NOT NULL;
CREATE INDEX barbar ON bar(a) WHERE a <> 0;

在这两种情况下,计划者都可以在选择索引时使用索引,= 10并在分别搜索NULL或0时使用seq扫描。两个部分索引的大小相同。并且,完整索引(未显示)的大小相同。按照相同的方法,我用一个序列1..1e5,,一个null / 0值和另一个序列加载了表1..1e5。两种方法都能够找到带有覆盖整个表的索引的null / 0。

TLDR;摘要

我无法以一种或另一种方式来证实我认为值得考虑的大多数性能问题,因为这些问题包括计划者的不足。使用null保存ram的好处是真实的。通过不使用null节省的磁盘空间可以忽略不计,这对于具有一NULLABLE列或少于8列的表来说是一个夸大的说法。在这些情况下,不会节省磁盘空间。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.