INSERT在Oracle中发出COMMIT之前的工作原理

时间:2012-02-05 05:26:12

标签: oracle insert cursor

我的问题是oracle在发出COMMIT之前如何处理INSERT事务。

当我正在进行INSERT事务时,oracle是否会等到我在该过程中插入所有记录,然后当我发出COMMIT语句时,是否会将记录保存在此事务的序列中?

在下面的代码中,第一个插入是行数(元数据),然后光标循环并开始插入实际数据。

有可能,在我调用此过程的一个事务中,首先插入我的元数据记录,然后插入一些其他数据(与此事务无关),然后插入其余的数据。因此,循环中的第一条记录和其余记录不会插入序列中。

-- This code belongs to proecdure when ever a user clicks on insert 
-- button from the front end form

DECLARE

    rowcnt NUMBER;

    CURSOR c_get_employ IS
    SELECT EMP.EMPLOYER_ID, EMP.EMPLOYER_NAME, EMP.EMPLOYER_LOCATION
          FROM EMP
            WHERE EMP.EMPLOYER_COUNTRY = 'USA'
    ORDER BY EMP.EMPLOYER_ID;

BEGIN

    Select count(*) 
    INTO rowcnt 
    FROM EMP
    WHERE EMP.EMPLOYER_COUNTRY = 'USA'
    ORDER BY EMP.EMPLOYER_ID;

    -- I want to insert the 'number of employee records' that will be inserted (metadata)

    INSERT INTO EMP_OUTPUT 
        (EMPID, EMPNAME, EMPLOC, ECOUNT)
    VALUES
        (,,,rowcnt);

    -- Then loop through the cursor and start inserting the data
    FOR c_post_employ IN c_get_employ LOOP

        INSERT INTO EMP_OUTPUT 
            (EMPID, EMPNAME, EMPLOC)
        VALUES
            (c_post_employ.EMPLOYER_ID,c_post_employ.EMPLOYER_NAME,c_post_employ.EMPLOYER_LOCATION);

    END LOOP;

    COMMIT;

END;

4 个答案:

答案 0 :(得分:4)

另一项交易可以同时对您的交易执行插入,但您的交易无法看到它们:

  • 直到另一个事务提交(如果您的事务使用READ COMMITTED隔离)或
  • (使用SERIALIZABLE隔离时) - 您需要启动另一个事务才能看到它们。

这是否会产生正确的行为,由你决定。


请注意SELECT COUNT(*) ... - 它可能无法返回您的期望。请考虑以下情形:

  • EMP表最初为空。
  • 事务A在EMP中启动并插入一行,但不提交。
  • 事务B启动并在EMP中插入一行,但不提交。
  • 事务A执行SELECT COUNT(*) FROM EMP并获得1(因为它看到了自己新插入的行,但由于B尚未提交,因此没有看到B'新插入的行)。
  • 事务B执行SELECT COUNT(*) FROM EMP并且也获得1(出于同样的原因但反之)。
  • 事务A将1插入EMP_OUTPUT并提交。
  • 事务B将1插入EMP_OUTPUT并提交(假设没有密钥违规)。

因此,尽管表实际上有2行,但仍插入1!

不幸的是,即使是Oracle的SERIALIZABLE隔离也不会让你免于这种异常现象。几乎是保证"正确"的唯一方法。结果if lock the entire table,因此不会发生并发插入(或删除)。

答案 1 :(得分:2)

如果可能,请使用单个SQL语句。它将具有语句级读取一致性,并且会更快。

insert into emp_output(empid, empname, emploc, ecount)
with employees as
(
    select employer_id, employee_name, employer_location
    from emp
    where employer_country = 'USA'
    order by employer_id    
)
select null, null, null, count(*) from employees
union all
select employer_id, employee_name, employer_location, null from employees;

答案 2 :(得分:1)

您想谷歌的术语是“读取一致性”:

http://docs.oracle.com/cd/B12037_01/server.101/b10743/consist.htm

底线:

  • 如您所知,如果您回滚,就好像插件“从未发生过”

  • 然而,其他东西可能(并且可能确实)在此期间“发生”。

答案 3 :(得分:1)

您需要在Serializable Isolation Level中运行:

http://docs.oracle.com/cd/E11882_01/server.112/e16508/consist.htm#BABCJIDI

“可序列化事务只查看事务开始时提交的那些更改,以及事务本身通过INSERT,UPDATE和DELETE语句所做的更改。可序列化事务不会遇到不可重复的读取或幻像。”