主键保证:重复和无效

Cha*_*rns 3 postgresql sqlite oracle sql-server

Oracle 允许主键具有重复值和空值。使用此功能并不是一个特别好的主意,但它意味着大多数开发人员认为主键的某些保证(非空、唯一)并不是真正的保证。

CREATE TABLE oracle_guarantees (
    ID NUMBER(9,0),
    NAME VARCHAR2(50 BYTE),
    breed NVARCHAR2(100)
);

INSERT INTO oracle_guarantees VALUES (1, 'Fuzz Head', 'Tabby');
INSERT INTO oracle_guarantees VALUES (2, 'Fluffy Thing', 'Mix');
INSERT INTO oracle_guarantees VALUES (2, 'Fluffy Thing', 'Mix');
INSERT INTO oracle_guarantees VALUES (3, 'Tiger', 'Tabby');
INSERT INTO oracle_guarantees VALUES (4, 'Fur Beast', 'Bengal');
INSERT INTO oracle_guarantees VALUES (5, 'Karate', 'Japanese Bobtail');
INSERT INTO oracle_guarantees VALUES (6, 'Chairman Meow', 'Chinese Harlequin');
INSERT INTO oracle_guarantees VALUES (NULL, 'No Cat', 'No breed');

CREATE INDEX oracle_guarantees_pk ON oracle_guarantees (ID);
ALTER TABLE oracle_guarantees ADD CONSTRAINT oracle_guarantees_pk PRIMARY KEY (ID) DISABLE KEEP INDEX;
ALTER TABLE oracle_guarantees MODIFY CONSTRAINT oracle_guarantees_pk ENABLE NOVALIDATE;
Run Code Online (Sandbox Code Playgroud)

SQLite PK 还允许一个空值但不允许重复值

是否可以将约束标记为在 Microsoft SQL 或 Postgres 中具有重复值或空值的主键?

换句话说,我是否可以绝对依赖记录的功能集中的 PK 唯一性和非空性(忽略手动编辑的数据文件、错误或修改的服务器源代码等情况)?


进一步说明:我怀疑 DBA 和开发人员的工作方式与我想象的略有不同。

思考练习:开发人员需要唯一标识任何表上的行(表结构在编译时是未知的)。 Oracle 关于主键约束的文档出现在搜索中并说:

主键约束在单个声明中结合了 NOT NULL 约束和唯一约束。也就是说,它禁止多行在同一列或列组合中具有相同的值,并禁止值为空。

然后,开发人员寻找一种在运行时查找任何表上的主键的方法。他们可能会遇到这样的问题:https : //stackoverflow.com/questions/9016578/how-to-get-primary-key-column-in-oracle

最高投票答案的查询产生:

PK元数据查询结果

我相信,任何合理的非 DBA 人员此时都会得出结论,该ID列不能有 NULL 或重复值。这个结论是不正确的。

  • 是甲骨文的错吗?不。
  • 桌子设计得当吗?不。
  • 创建这样一个表的语句是否复杂?非物质。
  • Id 列是“真正的”主键吗?也许不是,但这更像是一个哲学问题。ID 列出(并且被示出为“启动”)通过在顶级SO解释以上链接的问题查询。也许这个问题需要添加一个验证检查,但我从未见过有人在代码中检查过这个问题,也没有在该线程或我找到的相关线程中找到任何答案。

因此,在现实世界中,任意表都可以有一个all_constraints.constraint_type具有重复值的主键(根据 的定义)。和 NULL 值。

我现在知道软件还必须确保约束得到验证。优秀!这提供了我需要的保证

问题是:在 PostgreSQL 或 Microsoft SQL 中是否有可能有一个具有 NULL 或重复值的主键(由 DBMS 元数据定义)?

ype*_*eᵀᴹ 7

Oracle 允许主键具有重复值和空值。

并不真地。在一系列复杂的语句之后,您设法创建的是一个已启用但未验证的约束。这意味着 Oracle 将检查插入和更新(为了唯一性),但可能会留下现有的重复项。所以它只是名义上的PK。这是一个未经验证的约束,所以不是真正的 PK。

换句话说,我是否可以绝对依赖记录的功能集中的 PK 唯一性和非空性(忽略手动编辑的数据文件、错误或修改的服务器源代码等情况)?

更多上下文:我正在开发一个显示记录并允许用户在任意表上删除它们的应用程序。在处理任意表时,我需要使用元数据和其他方式来确定我有哪些保证。许多数据库开发都是关于保证的——保证一个大的事务将提交或不提交(但不是部分提交)。保证成功修改的行保持修改状态。并保证主键唯一地指向一行。

是的,您可以依赖,但前提是您的应用程序读取元数据和所有详细信息 - 这可能因 DBMS 不同而不同。

  • SQL Server 允许禁用约束吗?应用程序在读取和解释元数据表时必须考虑到这一点。

  • Oracle 允许禁用或未验证的约束吗?应用程序也必须相应地考虑这些选项。

  • Postgres 允许一些不同的奇怪场景?它也必须考虑它们。

另一个可能的选项(对您来说可能是也可能不是)是如果应用程序是创建、删除和修改数据库对象的唯一应用程序,或者是否所有执行此操作的应用程序都在您或单个控制之下。然后它可以依赖元数据的健全性(“只考虑唯一约束”)而不检查这些细节/罕见情况——因为它可以依赖一开始就没有创建这样的罕见情况。

根据你的结论:

是甲骨文的错吗?不。

我同意。我不知道为什么允许这样做,但它可能解决了一些问题。而且它可能只是暂时使用 - 例如在从另一个来源导入到数据库的过程中。

桌子设计得当吗?不。

创建这样一个表的语句是否复杂?非物质。

我也同意,在这两个方面。

Id 列是“真正的”主键吗?也许不是,但这更像是一个哲学问题。ID 由上面链接问题的评分最高的 SO 答案中的查询列出(并显示为“已启用”)。也许这个问题需要添加一个验证检查,但我从未见过有人在代码中检查过这个问题,也没有在该线程或我找到的相关线程中找到任何答案。

我们得出相同的结论:需要添加验证检查。


至于 SQL Server 和 Postgres 中还存在哪些其他可能性:

  • SQL 服务器:

    • PRIMARY KEY并且UNIQUE约束不能被禁用。
    • 可以禁用唯一索引。它们也可以在不检查的情况下重新启用,这将导致与 Oracle 中的“启用未验证”非常相似的情况。
    • FOREIGN KEY并且可以禁用CHECK约束。它们也可以在不检查的情况下重新启用。 有关详细信息,请参阅和禁用索引和约束。我在这个问题中的回答是:什么是解释了与 Oracle 不同的选项和语法。
      ALTER TABLEWITH CHECK CHECK CONSTRAINT
  • PostgreSQL:

    • PRIMARY KEYUNIQUE并且不能禁用EXCLUDE约束。
    • FOREIGN KEY并且CHECK约束不能被禁用。但是可以使用该NOT VALID选项创建它们(这意味着它们在不检查现有行的情况下启用,就像在 SQL Server 中一样)。详情请参阅ALTER TABLE