【问题标题】:Oracle Merge statement not inserting dataOracle Merge 语句未插入数据
【发布时间】:2017-01-18 08:55:51
【问题描述】:

我在 oracle 11g 上提出了以下用于合并数据的 sql 语句。

  MERGE INTO myTable tgt
USING ( SELECT myTable.ROWID AS rid
         FROM   myTable

        WHERE myTable.myRef = 'uuuu' or
        myTable.name = 'sam'
        --union
        --select rowId,null from dual
      ) src
ON (tgt.rowid = src.rid)
WHEN MATCHED THEN
update set tgt.myRef = 'uuuu',tgt.name='name_worked'
when not matched then 
insert (
tgt.myRef,tgt.name) values ('RRRR','HHH');

我需要在此处插入除 ID 列之外的数据,这将通过主键插入触发器进行管理。由于错误ORA-38104: Columns referenced in the ON Clause cannot be updated,我在这里使用了 RowId 方法。

现在我的问题是合并语句在更新时工作正常,但在这种情况下插入数据失败。

我浏览了这个post 并添加了联合声明。但是由于我的表中的约束而不是顺利运行它,在插入重复记录时仍然失败。

有人可以帮我吗?非常欣赏它。谢谢你。

==========已编辑============

请在下面找到完整的代码示例和错误消息。

MERGE INTO myTable tgt
USING ( SELECT myTable.ROWID AS rid
         FROM   myTable

        WHERE myTable.myRef = 'RRRR' or
        myTable.mytablename = 'sam'
        union
        select rowId from dual
      ) src
ON (tgt.rowid = src.rid)
WHEN MATCHED THEN
update set tgt.myRef = 'uuuu',tgt.mytablename='myt_name', tgt.name='nameA'
when not matched then 
insert (
tgt.mytableid,tgt.mytablename,tgt.name,tgt.myRef) values (11,'RRRR','HHH','myref1');

我的桌子是-

CREATE TABLE "sa"."MYTABLE"
  (
    "MYTABLEID"   NUMBER NOT NULL ENABLE,
    "MYTABLENAME" VARCHAR2(20 BYTE) NOT NULL ENABLE,
    "NAME"        VARCHAR2(20 BYTE),
    "MYREF"       VARCHAR2(20 BYTE),
    CONSTRAINT "MYTABLE_PK" PRIMARY KEY ("MYTABLEID", "MYTABLENAME")
  )

如果我第一次运行它,它将按预期插入记录。 当我第二次运行它时,我的期望是它应该匹配myRef = 'RRRR' 并进行更新。我故意在条件之间加上“或”,因为如果我发现表中存在任何值,它应该去更新现有记录。

但不会进行更新,而是会抛出此错误,因为合并语句尝试再次插入。

SQL Error: ORA-00001: unique constraint (sa.MYTABLE_PK) violated
00001. 00000 -  "unique constraint (%s.%s) violated"

我的要求是当它第一次运行时它应该插入,当我再次运行它时它应该选择该记录并更新它。请让我知道在我的合并语句中要调整什么,以便在此处按预期工作。提前致谢。

【问题讨论】:

  • 两个问题:1)您提到的“我的表中的约束”(和错误消息)是什么?如果您源中没有联合,ON 子句将始终找到匹配项 - 除非@987654322 @ - 并且不会发生任何插入。 2)您希望插入在哪种情况下发生?
  • 插入失败的错误信息是什么?
  • 可能是条件 tgt.rowid = src.rid 在所有情况下都满足
  • 请参阅上面已编辑的问题以及更多详细信息。非常感谢您的 cmets。
  • 您是否使用 ROWID 作为您的一个表中的列名?

标签: sql oracle oracle11g merge


【解决方案1】:

下面的 MERGE 查询就是您要查找的内容。

MERGE INTO myTable tgt
USING ( select x.rid from 
       (SELECT myTable.ROWID AS rid
        FROM   myTable 
        WHERE myTable.myRef IN ('myref1','uuuu')) x 
        right outer join dual 
        on x.rowid <> dual.rowid
      ) src
ON (tgt.rowid = src.rid)
WHEN MATCHED THEN
update set tgt.myRef = 'uuuu',tgt.mytablename='myt_name', tgt.name='nameA'
when not matched then 
insert (tgt.mytableid,tgt.mytablename,tgt.name,tgt.myRef) values (mytable_seq.nextval,'RRRR','HHH','myref1');

对您提供的查询进行了 3 处更改。

  1. 在 myTable.myRef 列中检查 'using' 子查询 'RRRR',但在插入时插入 'myref1'。因此,这已在使用子查询中更改以检查“myref1”。
  2. 'uuuu' 已在同一个检查中引入。
  3. 在右外连接中引入了 DUAL。

查询将如下处理 -

1.第一次运行时,mytable 中不会有行。因此,使用对偶的右外连接将占上风。这将使插入发生。

  1. 在第 2 次运行期间,将有一行 myRef 列为“myref1”。它的 Rowid 将被拾取并发生更新。更新后,myRef 列将更新为 'uuuu'。

3.在所有后续运行过程中,总是会在内部使用子查询返回 1 行,这一次是因为 'uuuu' 的列值。这将使更新发生,这将再次使用相同的现有值更新列。

因此,第一次 INSERT 将发生,并且在所有后续时间都将发生 UPDATE。

【讨论】:

  • 在这里感谢您的帮助。我有几个担忧。当表增长到数千条记录时会发生什么?然后我们必须将所有这些参数放在 IN 子句中,这是不可能的。此外,如果表有 20 列左右,在这种情况下性能会非常低。在这里感谢您的帮助。
  • 是这样的要求吗 - 1)如果已经有1000条记录,它将插入第1001条记录。那么当查询再次运行时,它会更新所有这 1001 条记录吗? 2) 'values' 列表中的值的来源是什么?
  • 是的,这个表可能有 1000 条记录。值列表将从外部传递(CSV 文件)。我将遍历它,如果任何现有列值与 CSV 中的相应行匹配,则需要使用从 CSV 设置的值更新表中的其他列(主键列除外)。
  • 您可以先将csv文件中的数据加载到临时表中。然后从“IN”列表中的该临时表中选择所需的值。您可以在 INSERT 查询的“VALUES”列表中使用相同的临时表。
【解决方案2】:

您似乎想要以下内容:

MERGE INTO MYTABLE t
  USING (SELECT newID, newTable_name from DUAL) d
    ON (t.MYTABLEID = d.newID AND
        t.MYTABLENAME = d.newTable_name)
  WHEN MATCHED THEN
    UPDATE SET NAME = newName,
               MYREF = newRef
  WHEN NOT MATCHED THEN
    INSERT (MYTABLEID, MYTABLENAME, NAME, MYREF)
    VALUES (newID, newTable_name, newName, newRef)

变量 newIDnewTable_namenewNamenewRef 是从您使用的任何数据源填充的。

另外 - 你真的希望主键是 (MYTABLEID, MYTABLENAME) 吗?您实际上可以有多行具有相同的 MYTABLEID 值吗?你真的想允许多行具有相同的 MYTABLENAME 值吗?我的想法是主键应该是 MYTABLEID,对 MYTABLENAME 有一个 UNIQUE 约束。如果是这种情况,那么 MYTABLEID 是多余的,而 MYTABLENAME 可以用作主键。 ????

祝你好运。

【讨论】:

  • 感谢您的回复,但就我而言,我需要检查 select 子句中的每一列。这意味着需要检查 OR 条件。如果有任何值匹配,我需要继续更新该特定记录。感谢您的帮助鲍勃。谢谢
【解决方案3】:

我能够在下面的查询中完成这项工作。

MERGE INTO myTable tgt
USING ( SELECT (SELECT myTable.ROWID AS rid 
         FROM   myTable

        WHERE myTable.myRef = '2' or
        myTable.mytablename = 'sam'
      ) as rid from dual)  src
ON (tgt.rowid = src.rid)
WHEN MATCHED THEN
update set tgt.myRef = 'uuuu',tgt.mytablename='test1', tgt.name='nameA'
when not matched then 
insert (
tgt.mytableid,tgt.mytablename,tgt.name,tgt.myRef) values (11,'RRRR1','1','2');

第一次插入行,后续运行时更新行。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-24
    • 2012-11-22
    • 2015-01-30
    • 2011-01-21
    • 2019-04-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多