Oracle Sequence wastes/reserves values (in INSERT SELECT)

时间:2015-11-12 11:57:14

标签: sql oracle select sequence sql-insert

I've been struggling with sequences for a few days. I have this Origin data table called "datos" with the next columns:

CENTRO
CODV
TEXT
INCIDENCY

And a Destiny data table called "anda" with the following:

TIPO  = 31 (for all rows)
DESCRI = 'Site'  (for all rows)
SECU  = sequence number generated with Myseq.NEXTVAL
CENTRO
CODV
TEXT

The last three columns must be filled in with data from "datos" table.

When I execute my query, it all works fine, my table is filled and the sequence generates its values. But, in the INSERT INTO SELECT, I have the following conditions: Every row in origin "datos" must not already be in the destiny "anda", so it won't be duplicated, and every row in "datos" must have the INCIDENCY flag value to 'N' or NULL. If each row matches the conditions, it should be filled.

The thing is, that the query works fine and I have been trying with many different values. Here comes the problem:

When a row has its INCIDENCY value set to 'Y' (so it must not be copied into destiny table), it doesn't appear, but the sequence DOES consumes one value, and when I check Myseq.NEXTVAL its value is higher.

How can I prevent the sequence to add any value when it doesn't match the conditions? I've read that Oracle first reserves all the possible values returning from the SELECT query, but I can't find how to prevent it.

Here's the SQL:

INSERT INTO anda (TIPO, DESCRI, SECU, CENTRO, CODV, TEXT)
      SELECT(  31 TIPO,
            'Site' DESCRI,
            Myseq.NEXTVAL,
            datos.CENTRO,
            datos.CODV,
            datos.TEXT
    FROM  datos
    WHERE (CENTRO, CODV) NOT IN
            (SELECT CENTRO, CODV
              FROM   anda)
    AND (datos.INCIDENCY = 'N' OR datos.INCIDENCY IS NULL)
    )

Thanks in advance!!

Definition of MySeq

 CREATE SEQUENCE  CREATE SEQUENCE  "BBDD"."MySeq" MINVALUE 800000000000
MAXVALUE 899999999999 INCREMENT BY 1 START WITH 800000000000 CACHE 20 ORDER  NOCYCLE ;

2 个答案:

答案 0 :(得分:2)

您可以使用CTE欺骗Oracle:

INSERT INTO anda (TIPO, DESCRI, SECU, CENTRO, CODV, TEXT)
    WITH toinsert as (
     SELECT d.*
     FROM  datos d
     WHERE (CENTRO, CODV) NOT IN (SELECT CENTRO, CODV FROM anda) AND
           (d.INCIDENCY = 'N' OR d.INCIDENCY IS NULL)
    )
    SELECT 31 as TIPO, 'Site' as DESCRI, Myseq.NEXTVAL,
            d.CENTRO, d.CODV, d.TEXT
    FROM toinsert d;

我不太确定这是否有用。更有保证的方法是使用before insert触发器(如果使用12c +,则使用标识列)。您可以增加触发器中的值。

但是,我同意休·琼斯的看法。您应该有信心使用序列为每行添加唯一值,此值将增加。由于其他原因(例如删除),可能会出现间隙。另外,我知道SQL Server在进行并行插入时可能会产生间隙。我不确定甲骨文是否也会这样。

答案 1 :(得分:2)

我不相信你有一个真正的问题(差距不是真正的问题)但是你可以在插入前加上 (在行级别)触发anda表上,并使用序列生成的值设置sequ

但请记住,这只会在声明中保持连续的序号。无论如何,你还会因其他原因而出现差距。

更新:正如Alex Poole评论的那样,插入本身不会产生间隙。 请参阅下面的测试:

> drop sequence tst_fgg_seq;
sequence TST_FGG_SEQ dropped.
> drop table tst_fgg;
table TST_FGG dropped.
> drop table tst_insert_fgg;
table TST_INSERT_FGG dropped.
> create sequence tst_fgg_seq start with 1 nocycle;
sequence TST_FGG_SEQ created.
> create table tst_fgg as select level l from dual connect by level < 11;
table TST_FGG created.
> create table tst_insert_fgg as
    select tst_fgg_seq.nextval 
    from tst_fgg
    where l between 3 and 5;
table TST_INSERT_FGG created.
> select * from tst_insert_fgg;
   NEXTVAL
----------
         1 
         2 
         3 

> insert into tst_insert_fgg 
    select tst_fgg_seq.nextval 
    from tst_fgg
    where l between 3 and 5;
3 rows inserted.
> select * from tst_insert_fgg;
   NEXTVAL
----------
         1 
         2 
         3 
         4 
         5 
         6 

 6 rows selected