【问题标题】:How to insert row in a table without multiple querying max(column_name)+1?如何在不多次查询 max(column_name)+1 的情况下在表中插入行?
【发布时间】:2019-10-28 16:37:13
【问题描述】:

我有一个表格,必须像这样插入数据:

SEQ      NAME      RUN_SL DOC_SL    DATE
1        UNIX       1       1    26/10/2019
2        UNIX       1       2    26/10/2019
3        UNIX       1       3    26/10/2019
4        ABCOP      2       1    26/10/2019
5        ABCOP      2       2    26/10/2019
6        TESLA      1       1    27/10/2019
7        LETHA      1       2    27/10/2019 
8        TESLA      2       1    27/10/2019
9        NEWBE      2       2    27/10/2019

数据应该按天插入。用户可以一次插入多行。在这种情况下,RUN_SL 将相同,但 DOC_SL 将增加。为了实现这一点,我必须始终这样看待:

SELECT NVL(MAX(A.RUN_SL), 0) + 1, NVL(MAX(A.DOC_SL), 0) + 1
INTO V_RUN_SL, V_DOC_SL
FROM TEST_TBL A
WHERE A.DATE = SYSDATE;

然后不得不使用循环插入数据:

FOR DATA_LIST..COUNT:
 INSERT INTO TEST_TBL
 VALUES(
   TBL_SEQ.NEXTVAL,
   DATA_LIST(i).NAME,
   V_RUN_SL,
   V_DOC_SL,
   SYSDATE
 );

我知道关于 NVL(MAX(Column_Name), 0) + 1 会有并发问题。为了实现正确插入,如何处理它并寻求替代建议来优化查询? 提前致谢。

【问题讨论】:

  • 老式的方法是使用序列。新奇的方法是使用generated always as identity
  • @GordonLinoff 到目前为止,我知道 12c 支持它。但我必须使用 11g。
  • 你可以使用+ row_number() over (order by whatever)代替+ 1
  • 您的 SELECT 语句与您陈述的要求不符。您能否首先在示例数据中说明哪些行被插入在一起? RUN_SL = 1 是否意味着前 3 行同时插入?

标签: sql oracle oracle11g


【解决方案1】:

我将做一些假设,而不是提问:

  • “DATE”只是日期部分,时间部分为 00:00:00
  • 今天有人第一次插入一行或多行,RUN_SL 应该是 1。
  • 下一次今天有人插入一些行,RUN_SL 应该是 2。
  • 每次 RUN_SL 递增时,DOC_SL 应从 1 重新开始。

现在说几句:

  • 您使用 SYSDATE。如果您只需要日期部分,请使用 TRUNC(SYSDATE)。
  • 要进行多次插入,请使用 FORALL 而不是 FOR。 FORALL 将调用 SQL 引擎一次,FOR 将每行调用一次。在 PL/SQL 和 SQL 之间来回切换会消耗大量 CPU。
  • 为避免并发问题,一次只能有一个会话可以进行插入。我在技术表上使用了一个简单的锁定机制来序列化操作。

下面的代码是一个简单的概念证明。输入数据被硬编码为插入 'UNIX' 3 次。

最好的问候,斯图阿什顿

create table test_tbl(
  SEQ number primary key,
  NME varchar2(16),
  RUN_SL number,
  DOC_SL number,
  DTE date
);
create sequence TBL_SEQ;
create table tbl_lock(n primary key check(n=1)) as
select 1 from dual;

declare
  type t_name is record(nme test_tbl.nme%type);
  type tt_name is table of t_name;
  data_list tt_name := new tt_name();

  l_run_sl test_tbl.run_sl%type;

  type tt_num is table of number;
  lt_num tt_num := new tt_num();

  procedure lock_tbl is
    l_num number;
  begin
    select n into l_num
    from tbl_lock
    for update wait 3;
  end lock_tbl;

begin
  lock_tbl;
  data_list.extend(3);
  lt_num.extend(3);
  for i in 1..data_list.count loop
    data_list(i).nme := 'UNIX';
    lt_num(i) := i;
  end loop;  
  select nvl(max(run_sl), 0) + 1 into l_run_sl
  from test_tbl
  where dte = trunc(sysdate);
  forall i in 1..data_list.count
    insert into test_tbl (seq, nme, run_sl, doc_sl, dte)
      values(tbl_seq.nextval, data_list(i).nme, l_run_sl, lt_num(i), trunc(sysdate));
end;
/
commit;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-17
    • 2018-09-30
    相关资源
    最近更新 更多