【问题标题】:How to create a primary key using a longer unique index?如何使用更长的唯一索引创建主键?
【发布时间】:2021-07-06 03:07:27
【问题描述】:

可以在创建主键时指定索引:

CREATE TABLE t (a NUMBER, b NUMBER);
ALTER TABLE t ADD PRIMARY KEY (a,b) USING INDEX (CREATE INDEX i ON t(a,b));

这也适用于列子集:

ALTER TABLE t ADD PRIMARY KEY (a) USING INDEX (CREATE INDEX i ON t(a,b));

我更喜欢唯一索引(因为非唯一索引会将 rowid 添加到每个键中,这会使索引更大且速度稍慢)。这也有效:

ALTER TABLE t ADD PRIMARY KEY (a,b) USING INDEX (CREATE UNIQUE INDEX i ON t(a,b));

但是,子集唯一索引会导致错误:

ALTER TABLE t ADD PRIMARY KEY (a) USING INDEX (CREATE UNIQUE INDEX u ON t(a,b));
ORA-14196: Specified index cannot be used to enforce the constraint.

总结一下:

OK     PRIMARY KEY (a,b) USING INDEX (        INDEX(a,b) )
OK     PRIMARY KEY (a,b) USING INDEX ( UNIQUE INDEX(a,b) )
OK     PRIMARY KEY (a)   USING INDEX (        INDEX(a,b) )
ERROR  PRIMARY KEY (a)   USING INDEX ( UNIQUE INDEX(a,b) )

我完全不明白为什么这是不可能的。

我经常需要它,例如对于具有两个主键列(比如国家、城市)和另一列(比如人口)的表。因为我总是查询另一列,所以三列索引是有意义的。由于前两列是唯一的(每个主键),索引也将是唯一的,但 Oracle 不会让我这样做。为什么?

【问题讨论】:

  • 有趣。我从未注意到向主键自动添加一列 [并且静默] 使其成为NOT NULL
  • 另外,有趣的是,Oracle 在第一个示例中创建索引INONUNIQUE
  • "...因为非唯一索引将 rowid 添加到每个键..." -- 你为什么这么说?所有索引都包含指向堆的指针。
  • @TheImpaler 它也会默默地删除 not null,请参阅 here
  • @TheImpaler re rowid:当然它有 rowid,但它显然也将它添加到键中,请参阅here

标签: sql oracle


【解决方案1】:

这是一个不适合 cmets 部分的评论,它可能是公然错误的。

我的看法是,Oracle 可以使用 1) 唯一索引或 2) 非唯一索引(可能由于历史原因而存在的功能性)来强制主键的唯一性。因此:

  • 案例#1:如果它使用一个唯一索引,那么找出一个值是否唯一的所有繁重工作都由索引本身完成。这是其功能的一部分。

  • 案例#2:如果它使用非唯一索引,则该索引用于存储值,但唯一性由扫描索引以查找多个值的主键约束本身验证。

现在,您的四个示例属于:

  • 案例 #1(非唯一)
  • 案例 #2(唯一)
  • 案例 #1(非唯一)
  • 不是第 1 种情况,也不是第 2 种情况。这就是我认为 Oracle 不允许这样做的原因。

当然,如果有人知道得更好,请纠正我。

【讨论】:

    【解决方案2】:

    列 (a,b) 上的唯一索引不能用于强制列 (a) 上的主键,并且数据库已正确阻止您执行此操作。

    这是因为 (1,100),(1,101),(1,102) 是 (a,b) 上的唯一索引中的合法值,其中强制列 a 仅包含 1,2,3,...等不能使用相同的索引进行操作。

    【讨论】:

    • 感谢您的回答!值 (1,100)、(1,101)、(1,102) 显然是非唯一索引中的合法值。那么为什么这个非唯一索引可以在 (a) 上强制执行主键,而不是唯一索引?
    • 我读得太快了。这是我的猜测,唯一索引可以保存 (null,null) 值,这就是它不起作用的原因。但是,如果在创建表时将 a 和 b 的列显式设为 NOT NULL,然后如果我要使用唯一索引创建主键约束,它可以解决
    • 我刚刚尝试了该选项(使列不为空),然后检查是否允许,但失败了
    • 总的来说,我猜是因为 rowid 不是唯一索引的一部分,并且因为唯一索引不能允许整个空值,因此它被阻止使用
    • @GeorgeJoseph Oracle 索引始终排除完全为空的条目,例如 (null, null)。实际上,这就是 Oracle 中用于生成过滤(也称为部分)索引的策略。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-10-03
    • 2013-09-13
    • 2014-09-01
    • 2013-08-02
    • 1970-01-01
    • 2013-05-10
    • 2017-04-28
    相关资源
    最近更新 更多