Как я могу определить фактический порядок вставки строк базы данных?

У меня есть многопоточный процесс, который вставляет несколько записей в одну таблицу. Вставки выполняются в хранимой процедуре, при этом последовательность генерируется INTO как переменная, и эта переменная позже используется внутри INSERT.

Учитывая, что я не делаю mysequence.nextval внутри самого INSERT, это заставляет меня думать, что два параллельных процесса могут получить последовательность в одном порядке, а затем выполнить вставку в обратном порядке. Если это так, то порядковые номера не будут отражать истинный порядок вставки.

Я также записываю sysdate в столбец DATE для каждой из моих вставок, но я заметил, что часто даты для двух записей совпадают, и мне нужно отсортировать по порядковому номеру, чтобы разорвать связь. Но, учитывая предыдущую проблему, это, похоже, не гарантирует фактического порядка вставки.

Как я могу определить абсолютный порядок вставки в базу данных?


person aw crud    schedule 16.07.2010    source источник
comment
Это своего рода отступление, но какое экономическое обоснование заставляет вас заботиться о заказе на размещение?   -  person Mark Peters    schedule 16.07.2010
comment
@Mark: я создаю страницу, чтобы пользователи могли видеть журнал того, что произошло в процессе, и хочу, чтобы записи были в хронологическом порядке.   -  person aw crud    schedule 16.07.2010
comment
Но допустим, вы одновременно вставляете строки A и B и делаете: seqn(A)-seqn(B)-insert(B)-insert(A). Единственный способ, которым ваши пользователи могли бы ЗАБОТИТЬСЯ об этом несоответствии (A находится перед B), - это если B был каким-то образом связан с A или зависел от него. Но они параллельны, поэтому я предполагаю, что они независимы. Это действительно то, о чем должны заботиться ваши пользователи? Просто солги им, в том смысле, что никакого реального приказа не происходит.   -  person Mark Peters    schedule 16.07.2010
comment
не используйте последовательности для определения порядка вставки строк. Если приложение перенесено на RAC, каждый узел получает фрагмент порядковых номеров, поэтому вы увидите что-то вроде первого узла: 1, 2, 3, 4, второго узла: 100, 101, 102, третьего узла: 200, 201, 202, 203.   -  person Shannon Severance    schedule 16.07.2010


Ответы (7)


Типы данных DATE измеряются только в секундах, а TIMESTAMP — в миллисекундах. Решит ли это проблему?

Согласно документам Oracle:

TIMESTAMP: значения года, месяца и дня даты, а также значения часов, минут и секунд времени, где Fractional_seconds_precision — это количество цифр в дробной части SECOND поля даты и времени. Допустимые значения Fractional_seconds_precision — от 0 до 9. Значение по умолчанию — 6. Формат по умолчанию определяется явно параметром NLS_DATE_FORMAT или неявно параметром NLS_TERRITORY. Размеры варьируются от 7 до 11 байт, в зависимости от точности. Этот тип данных содержит поля даты и времени: ГОД, МЕСЯЦ, ДЕНЬ, ЧАС, МИНУТА и СЕКУНД. Он содержит доли секунды, но не имеет часового пояса.

Принимая во внимание, что date не:

ДАТА: Действительный диапазон дат с 1 января 4712 г. до н.э. по 31 декабря 9999 г. н.э. Формат по умолчанию определяется явно параметром NLS_DATE_FORMAT или неявно параметром NLS_TERRITORY. Размер фиксированный и составляет 7 байт. Этот тип данных содержит поля даты и времени: ГОД, МЕСЯЦ, ДЕНЬ, ЧАС, МИНУТА и СЕКУНД. В нем нет долей секунды или часового пояса.

Конечно, сказав это, я не уверен, почему это имеет значение, когда записи были написаны, но это способ решить вашу проблему.

person MJB    schedule 16.07.2010
comment
Я, наверное, должен был сформулировать это немного по-другому. Я думаю, что я имею в виду, как я могу определить, в каком порядке выполнялись операторы вставки, а не порядок физической записи в файлы базы данных. Я думаю, что уменьшение до миллисекунд устранит неоднозначность для меня. - person aw crud; 16.07.2010
comment
На самом деле TIMESTAMP может уменьшаться до наносекунд. Это зависит от операционной системы. - person APC; 17.07.2010

Последовательность должна быть потокобезопасной:

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  

теперь в другом сеансе:

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     

Последовательность Oralce является потокобезопасной: http://download.oracle.com/docs/cd/B19306_01/server.102/b14231/views.htm#ADMIN020 «Если два пользователя одновременно обращаются к одной и той же последовательности, то порядковые номера, которые получает каждый пользователь, могут иметь пробелы, потому что порядковые номера также генерируются другим пользователем». числа не могут быть 1,2,3,4,5 (как в моем примере --> если вы боитесь этого, вы можете поднять кеш)

это также может помочь, хотя они не размещают свой источник: http://forums.oracle.com/forums/thread.jspa?threadID=910428 "последовательность увеличивается немедленно и навсегда, независимо от того, фиксируете ли вы транзакцию или откатываете ее. Параллельный доступ NextVal к последовательности всегда будет возвращать отдельные значения для каждого звонящий».

Если вы опасаетесь, что вставки будут не по порядку, и вам нужно значение последовательности, используйте предложение return:

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

тогда вы знаете, что он был вставлен прямо тогда и там.

person Harrison    schedule 16.07.2010

Происходит несколько эффектов. Современные компьютеры могут выполнять столько операций в секунду, что таймеры не успевают. Кроме того, получение текущего времени является несколько дорогостоящей операцией, поэтому у вас есть промежутки, которые могут длиться несколько миллисекунд, пока значение не изменится. Вот почему вы получаете одно и то же sysdate для разных строк.

Теперь, чтобы решить вашу проблему со вставкой. Вызов nextval для последовательности гарантированно удалит это значение из последовательности. Если два потока вызывают nextval несколько раз, вы можете получить чередующиеся числа (т. е. поток 1 увидит 1 3 4 7, а поток 2 увидит 2 5 6 8), но вы можете быть уверены, что каждый поток получит разные номера.

Поэтому, даже если вы не используете результат nextval немедленно, вы должны быть в безопасности. Что касается «абсолютного» порядка вставки в базу данных, это может быть трудно сказать. Например, БД может хранить строки в кеше, прежде чем записывать их на диск. Строки могут быть переупорядочены для оптимизации доступа к диску. Но пока вы назначаете результаты из nextval своим строкам в том порядке, в котором вы их вставляете, это не должно иметь значения, и они всегда должны отображаться вставленными по порядку.

person Aaron Digulla    schedule 16.07.2010

Хотя может существовать некоторая концепция порядка вставки в базу данных, концепции порядка поиска, безусловно, нет. Любые строки, которые возвращаются из базы данных, будут возвращаться в любом порядке, в котором БД сочтет целесообразным их вернуть, и это может иметь или не иметь НИЧЕГО общего с порядком, в котором они были вставлены в базу данных. Кроме того, порядок, в котором строки вставляются в БД, может практически не иметь отношения к тому, как они физически хранятся на диске.

Глупо полагаться на любой заказ из запроса к БД без использования предложения ORDER BY. Если вы хотите быть уверены в каком-либо порядке, вам необходимо поддерживать эту связь на формальном уровне (последовательности, временные метки и т. д.) в вашей логике при создании записей для вставки.

person Will Hartung    schedule 16.07.2010
comment
Извините, возможно, я недостаточно ясно выразился в своем вопросе. У меня есть ORDER BY в столбце даты, но, поскольку несколько записей имеют одинаковое значение даты, я также упорядочивал по идентификатору последовательности. - person aw crud; 16.07.2010

Если транзакции являются отдельными, это можно определить по псевдостолбцу ora_rowscn для таблицы.

[Изменить] Еще немного подробностей, и я удалю свой ответ, если он бесполезен - если вы не создали таблицу с нестандартным предложением «rowdependencies», у вас будут другие строки из блока, помеченные scn, так что это может вводить в заблуждение. Если вам действительно нужна эта информация без изменения приложения, вам придется перестроить таблицу с этим предложением.

person dpbradley    schedule 16.07.2010

Учитывая ваше описание проблемы, которую вы пытаетесь решить, я думаю, что последовательности будут в порядке. Если у вас есть два процесса, которые вызывают одну и ту же хранимую процедуру, а второй (в хронологическом порядке) завершается первым по какой-то причине, действительно ли это имеет значение? Я думаю, что порядок, в котором вызывались процедуры (который будет отражаться последовательностью (если вы не используете RAC)) будет более значимым, чем порядок, в котором они были записаны в базу данных.

Если вас действительно беспокоит последовательность, в которую были вставлены строки, вам нужно посмотреть, когда были выпущены коммиты, а не когда были выпущены операторы вставки. В противном случае у вас есть следующий сценарий как возможность:

  1. Транзакция 1 запущена
  2. Транзакция 2 запущена
  3. Транзакция 3 запущена
  4. Транзакция 2 вставки
  5. Транзакция 1 вставляет
  6. Транзакция 3 вставки
  7. Транзакция 3 фиксируется
  8. Транзакция 1 фиксируется
  9. Транзакция 2 фиксируется

В этом случае транзакция 1 была запущена первой, транзакция 2 вставлена ​​первой и транзакция 3 зафиксирована первой. Порядковый номер дает вам хорошее представление о порядке, в котором транзакции были запущены. Поле временной метки даст вам представление о том, когда вставки были выпущены. Единственный надежный способ получить порядок коммитов — это сериализовать записи в таблицу, что, как правило, является плохой идеей (убирает масштабируемость).

person Allan    schedule 16.07.2010

Вы должны (а) добавить метку времени к каждой записи и (б) переместить последовательность NEXTVAL в оператор INSERT.

Таким образом, когда вы запрашиваете таблицу, вы можете ORDER BY timestamp, id, что фактически будет порядком, в котором строки были фактически вставлены.

person Jeffrey Kemp    schedule 19.07.2010