Oracle - 对可空字段的唯一约束

时间:2016-05-03 20:33:43

标签: oracle

我想咨询/收集一些关于oracle中可空列的唯一约束定义的可能解决方案的想法。

我们有一个客户表

  • PK(ID),first_name,last_name非常明显
  • EXT_CODE是唯一的,在应用程序中可见,用于与第三方系统同步,意味着它是外部ID首次由其他系统提供,然后保持不变的整个生命周期
  • 示例:更新客户端设置first_name ='ABC',其中ext_code ='ABC'
+---+-----------+-----------+-----------+
|ID |FIRST_NAME |LAST_NAME  |EXT_CODE   |
+---+-----------+-----------+-----------+
|1  |Peter      |Pletan     |ABC        |
|2  |John       |Dollar     |DEF        |
|3  |Mia        |Zin        |GHI        |
|4  |Jasper     |Blau       |NULL       |
|5  |George     |Khan       |NULL       |
-----------------------------------------

到目前为止,一切正常,每个表都有唯一的EXT_CODE,因此在请求从外部系统进行更新时,始终只返回一行。当存在ext_code = null的客户端时,无法从外部系统维护它,因为where = null,永远不会返回任何内容。只有一个客户端具有相同的EXT_CODE,但是没有此EXT_CODE的任何数量的客户端(列可以为空)

现在来了困难的部分。 我决定,在此表中,可以存储更多(独立)客户的数据。出于这个原因,我添加了一个名为CUSTOMER_CODE的新列。 此代码将表分成几个独立的空间,而每个客户只能看到她的数据。

为此,引入了oracle vpd(虚拟专用数据库)。

  1. 每位客户都使用自己的oracle用户
  2. 登录时,客户代码已加载
  3. 谓词WHERE CUSTOMER_CODE ='my_code'(在步骤2中加载)附加到每个查询
  4. 修改后的表可能会跟随

    +---------------+---+-----------+-----------+-----------+
    |CUSTOMER_CODE  |ID |FIRST_NAME |LAST_NAME  |EXT_CODE   |
    +---------------+---+-----------+-----------+-----------+
    |C1             |1  |Peter      |Pletan     |ABC        |
    |C1             |2  |John       |Dollar     |DEF        |
    |C1             |3  |Mia        |Zin        |GHI        |
    |C1             |4  |Jasper     |Blau       |NULL       |
    |C1             |5  |George     |Khan       |NULL       |
    |C2             |6  |Paul       |Walker     |1          |
    |C2             |7  |Simon      |Sleeper    |2          |
    |C2             |8  |Lian       |Driver     |3          |
    |C2             |9  |Cor        |Pilot      |NULL       |
    |C2             |10 |Martin     |Oldman     |NULL       |
    ---------------------------------------------------------
    

    这被视为一般概述。当客户C1登录时,她只能看到1-5行,而C2 6-10。

    问题出现了

    1. 由于EXT_CODE上的UNIQUE约束,客户C1和C2不能具有相同的ext_code - 以下两行已经违反约束
    2. +---------------+---+-----------+-----------+-----------+
      |CUSTOMER_CODE  |ID |FIRST_NAME |LAST_NAME  |EXT_CODE   |
      +---------------+---+-----------+-----------+-----------+
      |C1             |1  |Peter      |Pletan     |ABC        |
      |C2             |2  |John       |Dollar     |ABC        |
      
      1. 这很容易修复,通过代替UNIQUE(ext_code),我做了UNIQUE(CUSTOMER_CODE,EXT_CODE),导致另一个问题 - 我再也没有2行,其中ext_code为空,因为C1,NULL和C1,NULL是从oracle的角度来看也一样。这些行的示例,ID = 4,5。我可以在客户介绍之前提供这些。
      2. 我现在的可能性是什么? 1.基于功能的索引(丢弃唯一约束) - 索引,如果any为null,则将两个值都设置为null,因此它根本不会被索引=>可能是一个解决方案,但索引不能与唯一约束相反

        1. 触发器 - 检查数据并抛出异常(仅当两个值都不为空时)

        2. 使ext_code不为空 - 在组合上放置常规唯一约束(ext_code,customer_code)=>不可行的选择

        3. 其他想法 - 我想听听你的意见。

1 个答案:

答案 0 :(得分:1)

您还没有说过您使用的是哪个版本的Oracle,但是从11g开始,您可以使用具有唯一约束的虚拟列:

alter table customer add (unq_col varchar2(24) -- or necessary size
  generated always as (case when ext_code is null then null
    else customer_code||'~'||ext_code end));
alter table customer add (constraint unq_col_con unique (unq_col));

生成的列可以按照您认为安全的方式构建 - 如果您可以识别永远不在其中一个列中的字符,或填充或任何合适的字符,则使用分隔符。

然后尝试复制客户中的代码失败:

update customer set ext_code = 'ABC' where ext_code = 'DEF'

Error report -
SQL Error: ORA-00001: unique constraint (SCHEMA.UNQ_COL_CON) violated
00001. 00000 -  "unique constraint (%s.%s) violated"

但是与不同的客户合作是可以的:

update customer set ext_code = 'ABC' where ext_code = '1';

1 row updated.