как работает INSERT перед выдачей COMMIT в Oracle - PullRequest
2 голосов
/ 05 февраля 2012

Мой вопрос заключается в том, как oracle обрабатывает транзакцию INSERT перед выполнением команды COMMIT.

Во время выполнения транзакции INSERT оракул будет ждать, пока я не вставлю все свои записи в эту процедуру, а затем, когда я выполню инструкцию 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 ]

4 голосов
/ 05 февраля 2012

Другая транзакция может выполнять вставки одновременно с вашей транзакцией, но ваша транзакция не увидит их:

  • до тех пор, пока другая транзакция не завершится (если ваша транзакция использует изоляцию 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 и фиксирует (при условии, что нет нарушения ключа).

Итак, 1 вставляется, несмотря на то, что таблица фактически имеет 2 строки!

К сожалению, даже в Oracle SERIALIZABLEизоляция спасет вас от такой аномалии.Практически единственный способ гарантировать «правильный» результат, если заблокировать всю таблицу , чтобы не было одновременных вставок (или удалений).

2 голосов
/ 05 февраля 2012

Используйте один оператор 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;
1 голос
/ 05 февраля 2012

Вам нужно работать на Сериализуемом уровне изоляции:

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

"Сериализуемые транзакции видят только те изменения, которые были зафиксированы в момент начала транзакции, плюс те изменения, которые были сделаны самой транзакцией с помощью операторов INSERT, UPDATE и DELETE. Сериализуемые транзакции не испытывают неповторяющихся операций чтения или фантомов."

1 голос
/ 05 февраля 2012

Термин, который вы хотите использовать в Google, - это «непротиворечивость чтения»:

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

Итог:

  • Как вы знаете, если вы выполняете откат, это как если бы вставки "никогда не происходили"

  • Тем не менее, другие вещи могут (и, вероятно, произошли) тем временем "случиться".

...