合并更新 oracle 无法获得一组稳定的行

Posted

技术标签:

【中文标题】合并更新 oracle 无法获得一组稳定的行【英文标题】:merge update oracle unable to get a stable set of rows 【发布时间】:2017-12-05 17:58:21 【问题描述】:

我正在尝试根据另一个表(内部联接)更新 Oracle 中的一个表,但匹配的字段匹配超过 1 行,所以我收到一个错误:

无法获得稳定的行集

    MERGE INTO C
    USING D
    ON (C.SYSTEM = D.SYSTEM)
    WHEN MATCHED THEN
    UPDATE SET C.REF_CD = D.CODE,
               C.REF_DT = TO_DATE('12/05/2017', 'MM/DD/YYYY')
       WHERE C.CODE = '123'
       AND D.CODE IS NOT NULL
       AND C.CLOSED = 'N'
       AND C.RCVD_DT >= TO_DATE('12/01/2017', 'MM/DD/YYYY')
       AND C.RCVD_DT <= TO_DATE('12/04/2017', 'MM/DD/YYYY')
       AND SUBSTR(C.SSN,7,3) >= D.FROM
       AND SUBSTR(C.SSN,7,3) <= D.TO;

作为 SELECT 语句,我可以使用内部连接提取此信息。但是作为合并语句,我得到了上述错误。 SYSTEM 是两个表中唯一匹配的数据。我怎样才能以不会出错的方式重写上面的内容?

【问题讨论】:

我不确定我是否明白你在说什么。表 D 的 SYSTEM 列中是否存在重复值?如果有(这就是你得到的错误的含义),那么你在尝试做的事情上有一个致命的逻辑缺陷;不管你用什么编程语言(你写什么查询等)你都做不到。想想看:你想把 C.REF_CD 设置为 D.CODE 的值,但是有两个或多个表 D 中的行,从中读取 D.CODE 的值。哪些值应该用于更新? Oracle 不会为您做出这样的选择! 顺便说一句,“不出错”是您所做工作的错误目标。 (不幸的是,许多开发人员只想避免错误,而不管生成的代码实际上是否正确)。你想要的是一个有意义的问题陈述(你的似乎没有,此时),然后正确地解决这个问题。 “不出错”很容易 - 您可以从连接返回的所有数据中选择最大或最小 D.CODE。 是您问题的正确答案吗?应该由业务用户而不是软件工程师来决定! D 表的 TO 和 FROM 列有 3 位数字。这是一个范围。它还有一个分配给该范围的 CODE 列。只有几个系统,因此系统在许多行中重复。 System 是 C 和 D 表中唯一匹配的列,因此我可以加入的唯一列。我正在查看 C 表中的 3 位数字并在 D 表中找到该范围,并根据 D 表中该范围的代码分配 C 表中的代码。由于两个表中的多个系统值匹配,因此发生错误。 如果我将上述语句写为 INNER JOIN SELECT 语句,它可以正常工作。但是由于您不能在 oracle UPDATE 语句中执行 INNER JOIN,所以我不能那样写,这会使我的生活变得更轻松。因此,我尝试将其编写为 oracle 接受的合并,但随后出现错误。 澄清一下,系统只有重复的条目。但是当你添加范围时,没有重复。但由于它是在系统上有效地加入,因此系统的重复行存在问题。 【参考方案1】:

我将说明这个错误的根源是什么。 考虑以下简单示例:

CREATE TABLE A_100(
  x_system int,
  val int
);

INSERT INTO a_100 values( 1, 100 );
INSERT INTO a_100 values( 2, 200 );

CREATE TABLE B_100(
  x_system int,
  val int
);

INSERT INTO b_100 values( 1, 1100 );
INSERT INTO b_100 values( 2, 2000 );
INSERT INTO b_100 values( 2, 3000 );

commit;

现在请考虑加入:

SELECT *
FROM A_100 a
JOIN B_100 b
ON a.x_system = b.x_system AND a.x_system = 1
;

| X_SYSTEM | VAL | X_SYSTEM |  VAL |
|----------|-----|----------|------|
|        1 | 100 |        1 | 1100 |

上述查询给出了表B_100 中的一条唯一记录。如果您在合并语句中使用此连接条件,则合并将运行而不会出现任何错误:

MERGE INTO A_100 a
USING B_100 b
ON ( a.x_system = b.x_system AND a.x_system = 1)
WHEN MATCHED THEN UPDATE SET a.val = b.val
;

1 row merged. 

现在请考虑以下加入:

SELECT *
FROM A_100 a
JOIN B_100 b
ON a.x_system = b.x_system AND a.x_system = 2
;

| X_SYSTEM | VAL | X_SYSTEM |  VAL |
|----------|-----|----------|------|
|        2 | 200 |        2 | 2000 |
|        2 | 200 |        2 | 3000 | 

对于来自A_100 的一条记录,上述连接给出来自B_100 的两条记录。 如果您尝试将 MERGE 与上述连接条件一起使用,您将得到以下结果:

MERGE INTO A_100 a
USING B_100 b
ON ( a.x_system = b.x_system AND a.x_system = 2)
WHEN MATCHED THEN UPDATE SET a.val = b.val
;

Error report -
ORA-30926: unable to get a stable set of rows in the source tables

Oracle 只是说你:

从左表查询一条记录返回两个值: 右表中的 2000 和 3000。 我不能将右表中的两个值分配给左表的单个标量字段,这是不可能的。 请更改连接条件,使其仅提供一个唯一的 从右表记录左表中的每条记录

【讨论】:

以上是关于合并更新 oracle 无法获得一组稳定的行的主要内容,如果未能解决你的问题,请参考以下文章

合并语句问题 - 错误无法在源表中获得一组稳定的行

无法在源表中获得一组稳定的行?

错误:ORA-30926:无法在源表中获得一组稳定的行

oracle 中报ora-30926 无法在源表中获得稳定的行 是怎么回事

oracle 中报ora-30926 无法在源表中获得稳定的行 是怎么回事

如何获得解码/未透视行的唯一计数(列到行)