Sql 如何确定实际的数据库行插入顺序?

Sql 如何确定实际的数据库行插入顺序?,sql,oracle,plsql,sequences,Sql,Oracle,Plsql,Sequences,我有一个多线程进程,它将多条记录插入到一个表中。插入是在存储过程中执行的,序列生成到变量中,该变量随后在插入中使用 考虑到我没有在INSERT本身中执行mysequence.nextval,我认为两个并发进程可以按一个顺序获取一个序列,然后按相反的顺序执行插入。如果是这种情况,则序列号将不会反映插入的真实顺序 我还将sysdate记录在DATE列中,用于每个插入项,但我注意到,两个记录的日期往往匹配,我需要按序列号排序以打破这一关系。但是考虑到前面的问题,这似乎不能保证实际的插入顺序 如何确定插

我有一个多线程进程,它将多条记录插入到一个表中。插入是在存储过程中执行的,序列生成到变量中,该变量随后在
插入中使用

考虑到我没有在
INSERT
本身中执行
mysequence.nextval
,我认为两个并发进程可以按一个顺序获取一个序列,然后按相反的顺序执行插入。如果是这种情况,则序列号将不会反映插入的真实顺序

我还将
sysdate
记录在
DATE
列中,用于每个插入项,但我注意到,两个记录的日期往往匹配,我需要按序列号排序以打破这一关系。但是考虑到前面的问题,这似乎不能保证实际的插入顺序


如何确定插入数据库的绝对顺序?

日期数据类型仅为秒,而时间戳为毫秒。这能解决问题吗

根据Oracle的文档:

时间戳:年、月和日值 日期,以及小时、分钟和 时间的第二个值,其中 分数秒精度是 小数中的位数 第二个datetime字段的一部分。 可接受的价值观 分数秒精度为0到 9默认值为6。默认格式由 NLS\u日期\u格式参数或 由NLS_领土隐含 参数大小从7到10不等 11字节,取决于精度。 此数据类型包含日期时间 字段年、月、日、小时、分钟, 第二。它包含分数 秒,但没有时区

日期
不:

日期:有效日期范围为1月1日起, 公元前4712年至公元9999年12月31日 默认格式已确定 通过NLS_日期_格式显式 参数或由 NLS_区域参数。尺寸是 修正为7字节。此数据类型 包含年份的日期时间字段, 月、日、时、分、秒。 它没有分数秒或秒 时区


当然,话虽如此,我不知道为什么在写记录时它很重要,但这是一种可能解决您问题的方法。

有几种影响。今天的计算机每秒可以执行如此多的操作,以至于计时器跟不上。此外,获取当前时间是一项成本较高的操作,因此在值更改之前,间隔可能会持续几毫秒。这就是为什么不同的行会得到相同的
sysdate

现在来解决插入问题。对序列调用
nextval
,可以保证从序列中删除此值。如果两个线程多次调用
nextval
,您可以得到交错的数字(即线程1将看到1 3 4 7,线程2将看到2 5 6 8),但您可以确保每个线程将获得不同的数字


因此,即使您不立即使用
nextval
的结果,您也应该是安全的。至于数据库中的“绝对”插入顺序,这可能很难说。例如,DB可以在将行写入磁盘之前将行保存在缓存中。行可以重新排序以优化磁盘访问。但是,只要您按照插入行的顺序将
nextval
的结果分配给您的行,这应该无关紧要,而且它们应该总是按顺序插入。

虽然数据库中可能有插入顺序的概念,但肯定没有检索顺序的概念。从数据库返回的任何行都将以DB认为适合返回它们的任何顺序返回,这可能与它们插入数据库的顺序有关,也可能与它们无关。此外,行插入数据库的顺序可能与它们在磁盘上的物理存储方式几乎没有关系


依赖DB查询中的任何订单而不使用ORDERBY子句是愚蠢的。如果希望确定任何顺序,则在创建插入记录时,需要在逻辑中以正式级别(序列、时间戳等)维护该关系。

如果事务是独立的,则可以从表的ora_rowscn伪列确定

[编辑]
更多细节,如果这没有用,我将删除我的答案-除非您使用非默认的“rowdependencies”子句创建表,否则您将使用scn标记块中的其他行,因此这可能会产生误导。如果您确实希望在不更改应用程序的情况下获得此信息,则必须使用此子句重新生成表。

序列应该是线程安全的:

create table ORDERTEST (
    ORDERID number not null ,
    COLA   varchar2(10) ,
    INSERTDATE date default sysdate,
    constraint ORDERTEST_pk primary key (orderid)
) ;

create sequence ORDERTEST_seq start with 1 nocycle nocache ;

insert into ORDERTEST (ORDERID, COLA, INSERTDATE)
                select ORDERTEST_SEQ.NEXTVAL , substr(OBJECT_NAME,1,10), sysdate 
                  from USER_OBJECTS 
                 where rownum <= 5; --just to limit results

select * 
  from ORDERTEST
 order by ORDERID desc ;

 ORDERID                COLA       INSERTDATE                
---------------------- ---------- ------------------------- 
5                      C_COBJ#    16-JUL-10 12.15.36        
4                      UNDO$      16-JUL-10 12.15.36        
3                      CON$       16-JUL-10 12.15.36        
2                      I_USER1    16-JUL-10 12.15.36        
1                      ICOL$      16-JUL-10 12.15.36  

然后你就知道它当时就被插入了。

鉴于你对你试图解决的问题的描述,我认为序列会很好。如果有两个进程调用同一个存储过程,而第二个(按时间顺序)进程由于某种原因首先完成,那么这是否真的相关?我认为调用过程的顺序(这将由序列反映(除非您使用RAC))比它们写入数据库的顺序更有意义

如果您真的担心插入行的顺序,那么您需要查看提交是何时发出的,而不是inserts语句是何时发出的。否则,可能会出现以下情况:

  • 事务1已启动
  • 事务2已启动
  • 事务3已启动
  • 事务处理2插入
  • 事务处理1插入
  • 事务3插入
  • 事务3提交
  • 事务1提交
  • 事务2提交
  • 在这个
    insert into ORDERTEST (ORDERID, COLA, INSERTDATE)
                select ORDERTEST_SEQ.NEXTVAL , substr(OBJECT_NAME,1,10), sysdate 
                  from USER_OBJECTS 
                 where rownum <= 5; --just to limit results
    
    select * 
      from ORDERTEST
     order by ORDERID desc ;
    
     5 rows inserted
    ORDERID                COLA       INSERTDATE                
    ---------------------- ---------- ------------------------- 
    10                     C_COBJ#    16-JUL-10 12.17.23        
    9                      UNDO$      16-JUL-10 12.17.23        
    8                      CON$       16-JUL-10 12.17.23        
    7                      I_USER1    16-JUL-10 12.17.23        
    6                      ICOL$      16-JUL-10 12.17.23     
    
    declare 
    x number ;
    begin 
    insert into ORDERTEST (ORDERID, COLA, INSERTDATE)
                    values( ORDERTEST_SEQ.NEXTVAL , 'abcd', sysdate)
                    returning orderid into x;
    
    dbms_output.put_line(x);
    end;
    
    --11