【问题标题】:Merge statement issue in oracleoracle中的合并语句问题
【发布时间】:2016-01-02 05:12:39
【问题描述】:

我来自 Microsoft SQL 环境。我有两个表 tak_netak_beb,我的要求是如果值不存在,则插入从 tak_bebtak_ne 的值,如果存在则更新。所以我做了一个如下所示的合并语句。但是我现在面临的问题是非常天 50000 计数正在增加序列号。Oracle 是稳定的数据库,我不知道他们为什么这样做。所以我创建了一个函数并防止增加序列号。我的问题是,创建函数是否正确。以下是我所做的

merge into tak_ne a using tak_beb b ON (a.NAME=b.NAME)
When matched then
   Update
   Set a.AC_NO = b.AC_NO
       a.LOCATION = b.LOCATION
       a.MODEL = b.MODEL
When not matched then
insert
(
sl_no,
AC_NO,
LOCATION
MODEL
)
Values
(
s_slno_nextval
b.AC_NO
b.LOCATION
b.MODEL
)

然后我创建了一个函数

CREATE OR REPLACE FUNCTION s_slno_nextval
      RETURN NUMBER
    AS
      v_nextval NUMBER;
    BEGIN
      SELECT s_emp.nextval
      INTO   v_nextval
      FROM   dual;
      RETURN v_nextval;
  END;

【问题讨论】:

  • Oracle 12 作为标识列

标签: sql oracle sql-merge


【解决方案1】:

Oracle 使用这种方法为语句插入的每一行生成唯一 ID。您的 TAK_BEB 表可能有 50000 行,因此序列递增 50000 次。

将增量隐藏到函数中没有帮助。对每一行调用并执行函数,它再次将序列递增 50000 次。而且它增加了从双表中进行 50000 次选择的开销。

如果您确实需要对语句插入的所有行使用序列中的一个值,请使用包变量:

create package single_id_pkg is
  id  Number;
  function get_id return number;
end;
/

create or replace package body single_id_pkg is
  function get_id return number is
  begin
    return id;
  end;
end;
/

现在例如在表上使用语句前触发器来设置变量:

create trigger tak_ne_BSI_trg
before insert
on tak_ne
begin
  select s_emp.nextval
    into single_id_pkg.id
    from dual;
end;

插入触发器有一个缺点 - 使用 MERGE 子句,即使该语句只更新行,它也会触发(请参阅https://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:25733900083512)。如果有问题,则必须以其他方式初始化变量。

接下来修改您的语句以使用包变量:

merge into tak_ne a
  using tak_beb b
  on (a.NAME=b.NAME)
when matched then
  update
    set a.AC_NO = b.AC_NO
        a.LOCATION = b.LOCATION
        a.MODEL = b.MODEL
when not matched then
  insert (sl_no,
          AC_NO,
          LOCATION,
          MODEL)
  values (single_id_pkg.get_id
          b.AC_NO,
          b.LOCATION,
          b.MODEL)

【讨论】:

  • 我将对您的建议进行研发。平均而言,我在问题中所做的一切都有助于阻止序列每次增加,直到我进行插入。但我会按照您的方式尝试
  • 这将为每个新行使用相同的 ID,我不确定这是否是 OP 想要的。我知道他想避免为每个 UPDATED 行生成一个序列,但是每个 INSERTED 行应该获得相同的 ID 还是不同的 ID?
  • 是的,在这种情况下,@Mikhailov 提供的更好的解决方案 - 直接在触发器中从序列中设置 id - 在从序列中选择之前检查 id 是否不为空,因为 MERGE 语句会触发更新和插入触发器对于每一行
【解决方案2】:

在 Oracle 中,使用自动增量字段的标准方法是使用序列。当然,每次你想使用它时它都会增加序列号。 但是您可以省略调用sequence_name.nextval,将其隐藏在触发器中也被认为是标准方法。

CREATE OR REPLACE EDITIONABLE TRIGGER TAK_NE_ID_TR"
BEFORE INSERT ON tak_ne
FOR EACH ROW
BEGIN
IF :old.sl_no IS NULL THEN
  :new.sl_no := s_emp.nextval;
END IF;
END;

如果您想为一批插入添加相同的 id,您可以使用全局临时表来保存它。例如,像这样:

create global temporary table tak_ne_id ("id" number) on commit delete rows

create or replace trigger tak_ne_BSI_trg
before insert
on tak_ne
begin
 insert into tak_ne_id("id")
 values(s_emp.nextval);
end

create or replace TRIGGER TAK_NE_ID_TR
BEFORE INSERT ON tak_ne
FOR EACH ROW
BEGIN
if :old.sl_no is null then
  SELECT "id"
  INTO   :new.sl_no
  FROM   tak_ne_id;
end if;
END;

然后你可以像以前一样使用你的合并,而无需调用nextval

 merge into tak_ne a using tak_beb b ON (a.NAME=b.NAME)
    When matched then
       update
       set a.AC_NO = b.AC_NO,
           a.LOCATION = b.LOCATION,
           a.MODEL = b.MODEL
    When not matched then
    insert
    (
    AC_NO,
    LOCATION,
    MODEL
    )
    Values
    (
    b.AC_NO,
    b.LOCATION,
    b.MODEL
    );

【讨论】:

    猜你喜欢
    • 2013-05-11
    • 2021-08-09
    • 2021-12-04
    • 1970-01-01
    • 1970-01-01
    • 2014-12-19
    • 1970-01-01
    • 2020-12-22
    • 1970-01-01
    相关资源
    最近更新 更多