如何约束数据库表,使一列中只有一行可以具有特定值?
Posted
技术标签:
【中文标题】如何约束数据库表,使一列中只有一行可以具有特定值?【英文标题】:How to constrain a database table so only one row can have a particular value in a column? 【发布时间】:2010-09-15 23:39:04 【问题描述】:使用 Oracle,如果列值可以是“YES”或“NO”,是否可以约束表,以便只有一行可以具有“YES”值?
我宁愿重新设计表结构,但这是不可能的。
[UDPATE] 遗憾的是,此表中不允许使用空值。
【问题讨论】:
哦,亲爱的 - 不允许使用空值 - 这会稍微改变一些 - 现在您必须使用基于函数的索引 (@Tony Andrews)。仍然避免触发和自主事务。 【参考方案1】:它不适用于表定义。
但是,如果您使用调用存储过程的触发器更新表,则可以确保只有一行包含“YES”。
-
将所有行设置为“否”
将你想要的行设置为YES
【讨论】:
-1 用于基于触发器的解决方案。它们在强制执行表级约束方面效果不佳【参考方案2】:这是一个笨拙的 hack,但如果该列允许 NULL,那么您可以使用 NULL 代替“NO”并像以前一样使用“YES”。将唯一键约束应用于该列,您将永远不会得到两个“YES”值,但仍然有很多 NO。
更新:@Nick Pierpoint:建议添加检查约束,以便将列值限制为“YES”和 NULL。语法都在他的回答中制定出来。
【讨论】:
没有什么笨拙的 - 这就是要走的路。 +1 您还需要在表上添加一个检查约束,这样它就不允许除“YES”或 null 之外的任何内容。 如果你想让它看起来不错,你也可以用 NVL 包裹一个视图,然后你会得到你的 Y/N 好吧,如果你想加入它,使用 NULL 是很麻烦的。这可能是一个值得做的事情,但它仍然是一个设计妥协,远离更正确的方法。【参考方案3】:Oracle 是否支持类似过滤索引(上周我听说例如 MSSQL2008 支持)?也许您可以定义一个唯一键,它仅适用于列中值为“Yes”的行。
【讨论】:
没有过滤索引,但 FBI 提供了一种更灵活(如果可以说不太简洁)的方式来做同样的事情。【参考方案4】:使用基于函数的索引:
create unique index only_one_yes on mytable
(case when col='YES' then 'YES' end);
Oracle 只索引不完全为空的键,这里的 CASE 表达式确保所有 'NO' 值都更改为空值,因此不被索引。
【讨论】:
【参考方案5】:我想我会使用第二个表来指向您当前表中的相应行。另一个表也可以用来存储其他变量的值。
【讨论】:
难以在一致的环境中维护【参考方案6】:根据我对 yukondude 之前的回答的评论,我将添加一个唯一索引和一个检查约束:
create table mytest (
yesorno varchar2(3 char)
);
create unique index uk_mytest_yesorno on mytest(yesorno);
alter table mytest add constraint ck_mytest_yesorno check (yesorno is null or yesorno = 'YES');
【讨论】:
我认为只要 NULL 适合作为“不是”值,此方法就比 FBI 方法具有优势,因为优化器至少可以利用约束。我认为以 yesorno='YES' 和 yesorno 为空的谓词查询将获得更好的基数估计。 由于只有 1 行带有“YES”并且每隔一行带有 null,那么您将获得用于查找“YES”行和完整扫描的索引(非常正确)得到其他一切(“不”)。 当然——这只是一个问题,用 Null 替换“否”是否是实现这一目标的一个值得妥协的方案。【参考方案7】:您将需要查看 Tom Kyte 的文章,其中确切地提出了这个问题及其答案:
http://tkyte.blogspot.com/2008/05/another-of-day.html
总结:不要使用触发器,不要使用自治事务,使用两个表。
如果您使用 Oracle 数据库,那么您必须了解 AskTom 并获取他的书籍。
【讨论】:
所提出的问题与此略有不同,在汤姆的问题中,表格可以有多个“Y”,但每个国家/地区只有 1 个。当我读到它时,在这个例子中,表只能有 1 是,在这种情况下索引解决方案似乎有效。但是,是的,AskTom 是 Oracle 数据库的必备工具。 我同意。如果汤姆被问到这个问题,我猜他肯定会选择索引解决方案。以上是关于如何约束数据库表,使一列中只有一行可以具有特定值?的主要内容,如果未能解决你的问题,请参考以下文章
sql中如何使一列中的多个重复数据只显示一次, 求大神指导,使得图中的班简名重复的只显示一次。