ORA-00001:违反了唯一约束(MYUSER.ADI_PK)

问题描述 投票:1回答:2

我有两张桌子,adv_institutioninstitutioninstitution有5000多行,而adv_institution有1400多行

我想使用Oracle MERGE将记录从adv_institution回填到institution。这两个表有大约四个常见的字段,我可以用来回填。

这是我的整个MERGE声明

merge into
  adv_institution to_t
using (
  select
    uni.*,
    adv_c.country_cd as con_code_text
  from
    (
      select 
        institution_cd, 
        name,
        institution_status,
        country_cd
      from 
        institution uni
      where
        uni.institution_status = 'ACTIVE' and
        uni.country_cd is not null
      group by
        institution_cd,
        name,
        institution_status,
        country_cd
      order by
        name  
    ) uni,
    country_cd c_cd,
    adv_country adv_c
  where
    uni.country_cd = c_cd.country_cd and
    c_cd.description = adv_c.country_cd
) from_t
on
(
  to_t.VENDOR_INSTITUTION_CD = from_t.INSTITUTION_CD or
  to_t.INSTITUTION_CD = from_t.NAME
)
WHEN NOT MATCHED THEN INSERT (
  to_t.INSTITUTION_CD,
  to_t.INSTITUTION_NAME,
  to_t.SHORT_NAME,
  to_t.COUNTRY_CD,

  to_t.NOTE,
  to_t.UNIT_TERMINOLOGY,
  to_t.COURSE_TERMINOLOGY,
  to_t.CLOSED_IND,

  to_t.UPDATE_WHO,
  to_t.UPDATE_ON,
  to_t.CALLISTA_INSTITUTION_CD
)
VALUES (
  from_t.NAME,
  from_t.NAME,
  '',
  from_t.con_code_text,

  '',
  'UNIT',
  'COURSE',
  'N',

  'MYUSER',
  SYSDATE,
  from_t.institution_cd
);

我得到的错误是

错误报告 - ORA-00001:违反了唯一约束(MYUSER.ADI_PK)

ADI_PK意味着adv_institution.institution_cd是主键,它必须是唯一的。

那是因为在WHEN NOT MATCHED THEN INSERT中有一个插入语句。我将from_t.NAME插入to_t.INSTITUTION_CD

当插入from_t.NAME时,to_t.INSTITUTION_CD看起来至少有两次相同的值

但我做了一个小组声明,以确保from_t.NAME是独一无二的:

(
      select 
        institution_cd, 
        name,
        institution_status,
        country_cd
      from 
        institution uni
      where
        uni.institution_status = 'ACTIVE' and
        uni.country_cd is not null
      group by
        institution_cd,
        name,
        institution_status,
        country_cd
      order by
        name  
    ) uni

我不确定我是否正确理解了这个问题。我尽我所能,但仍然没有运气。

oracle oracle11g
2个回答
0
投票

我认为你的主要问题是分组。

请考虑以下示例:

 desc temp_inventory;
 Name                  Type
 --------------------- -----------
 WAREHOUSE_NO          NUMBER(2)
 ITEM_NO               NUMBER(10)
 ITEM_QUANTITY         NUMBER(10)

WAREHOUSE_NO    ITEM_NO    ITEM_QUANTITY
1               1000       100
1               2000       200
1               2000       300

如果我写一个查询,我想仓库_no是唯一的:

select warehouse_no,item_quantity 
from temp_inventory 
group by warehouse_no,item_quantity

它将返回相同的3行..而不是我想分组..

select warehouse_no,sum(item_quantity)
from temp_inventory 
group by warehouse_no

这将使Warehouse_no在这种情况下独一无二!

此外,如果您有VARCHAR2列,则可以使用MAX,MIN作为聚合函数以及group by在查询中创建唯一键。

例:

Select object_type, min(object_name) 
from user_objects group by object_type;

这将使object_type唯一并为其返回1个相应的对象名称。

所以请注意,如果有副本,最后将根据聚合函数消除一些记录。


0
投票

“但我做了一个小组声明,以确保from_t.NAME是独一无二的:”

但是您的查询不会这样做。它产生了一组独特的(institution_cd,name,institution_status,country_cd)组合。很明显,这样的集合可能包含name的多次重复,每个不同的country_cd值。由于您的密钥中有四个元素,因此您几乎可以保证您的集合将多次出现name

你可以在ON条件下将其与or复合,这意味着如果to_t.VENDOR_INSTITUTION_CD = from_t.INSTITUTION_CD,即使目标表中已存在to_t.INSTITUTION_CD = from_t.NAME记录,也会触发UNMATCHED逻辑。

问题是MERGE语句是原子的。来自USING子查询的记录集必须包含唯一键。当Oracle在结果集中发现第二次出现相同的name时,它没有说,我已经合并了其中一个,让我们跳过它。它必须抛出ORA-00001,因为Oracle无法知道哪个记录适用,(institution_cd,name,institution_status,country_cd)的哪个组合是正确的。

要解决此问题,您需要更改USING查询以生成具有唯一键的结果集。这是您的数据模型,您了解其业务规则,因此您可以正确地重写它。但也许是这样的:

  select 
    name,
    max(institution_cd) as institution_cd,
    institution_status,
    max(country_cd) as country_cd
  from (
    institution uni
  where
    uni.institution_status = 'ACTIVE' and
    uni.country_cd is not null
  group by
    name,
    institution_status
  order by
    name  
) uni

然后,您可以将MERGE ON子句简化为:

on
(
  to_t.INSTITUTION_CD = from_t.NAME
)

在子查询中使用MAX()是一种不优雅的kludge。我希望你能应用更好的商业规则。

© www.soinside.com 2019 - 2024. All rights reserved.