Оракул даты Oracle во время обновления - PullRequest
6 голосов
/ 11 августа 2011

Я перенесу некоторые данные из одной схемы / таблицы Oracle в новую схему / таблицу в той же базе данных.

Сценарий миграции выполняет следующие действия:

create table newtable as select
  ...
  cast(ACTIVITYDATE as date) as ACTIVITY_DATE,
  ...
FROM oldtable where ACTIVITYDATE > sysdate - 1000;

Если япосмотрите на исходные данные, все выглядит хорошо - вот одна запись:

select 
  activitydate,
  to_char(activitydate, 'MON DD,YYYY'),
  to_char(activitydate, 'DD-MON-YYYY HH24:MI:SS'),
  dump(activitydate),
  length(activitydate)
from orginaltable  where oldpk =  1067514

Результат:

18-NOV-10                 NOV 18,2010                        18-NOV-2010 12:59:15                          Typ=12 Len=7: 120,110,11,18,13,60,16  

Переносимые данные, показывающие, что данные повреждены:

select 
  activity_date,
  to_char(activity_date, 'MON DD,YYYY'),
  to_char(activity_date, 'DD-MON-YYYY HH24:MI:SS'),
  dump(activity_date),
  length(activity_date)
from newtable
where id =  1067514

Результат:

18-NOV-10                 000 00,0000                         00-000-0000 00:00:00                           Typ=12 Len=7: 120,110,11,18,13,0,16   

Около 5000 из 350 тыс. Записей показывают эту проблему.

Кто-нибудь может объяснить, как это произошло?

Ответы [ 2 ]

4 голосов
/ 12 августа 2011

ОБНОВЛЕНИЕ:

Я не нашел опубликованной ссылки на этот конкретный тип повреждения DATE на сайте поддержки Oracle.(Возможно, мои быстрые поиски просто не включили его.)

  • Скрипт Baddate для проверки базы данных на наличие поврежденных дат [ID 95402.1]
  • Ошибка 2790435 - Serial INSERTпри параллельном SELECT и преобразовании типов можно вставлять поврежденные данные [ID 2790435.8]

Вывод функции DUMP () показывает, что значение даты действительно недопустимо:

Typ=12 Len=7: 120,110,11,18,13,0,16 

Мы ожидаем, что байт минут должен быть значением от одного до шестидесяти, а не ноль.

7 байтов значения DATE представляют, в порядке, век (+100), год (+100),месяц, день, час (+1), минуты (+1), секунды (+1).

Единственный раз, когда я видел недопустимые значения DATE, подобные этому, когда значение DATE предоставлялось как переменная связыванияиз программы Pro * C (где значение связывания предоставляется во внутреннем 7-байтовом представлении, полностью обходя обычные процедуры проверки, которые перехватывают недопустимые даты, например, 30 февраля)

Нет причин ожидать, что вывидишь, учитывая оракулсинтаксис, который вы опубликовали.

Это либо ложная аномалия (повреждение памяти?), либо, если это повторяется, то это ошибка (ошибка) в коде Oracle.Если это недостаток в коде Oracle, наиболее вероятными подозрениями будут «новые» функции в не пропатченном выпуске.

(я знаю, что CAST - это стандартная функция SQL, которая давно использовалась в других базах данных.Я предполагаю, что я старая школа, и никогда не вводил его в свой репертуар синтаксиса Oracle.Я не знаю, какая версия Oracle представила CAST, но я бы остался в стороне от него в первом выпуске, который появилсяв.)


Большой «красный флаг» (который заметил другой комментатор) состоит в том, что CAST( datecol AS DATE).

Вы ожидаете, что оптимизатор будет рассматривать это как эквивалент date_col ..... но прошлый опыт показывает, что оптимизатор интерпретирует TO_NUMBER( number_col ) как TO_NUMBER( TO_CHAR ( number_col ) ).

Я подозреваю, что с этим ненужным CAST может происходить нечто подобное.


На основена той одной записи, которую вы показали, я подозреваю, что проблема связана со значениями со значением «59» для минут или секунд, и, возможно, со значением «23» для часов, которые будут показывать ошибку.

Я бы попробовал проверить места, где минуты, часы или секунды хранятся как 0:

SELECT id, DUMP(activitydate)
  FROM newtable
 WHERE DUMP(activitydate) LIKE '%,0,%' 
    OR DUMP(activitydate) LIKE '%,0'
2 голосов
/ 12 августа 2011

Я видел похожие вещи на spence7593, опять же с Pro * C. Можно создать недействительные даты программно с помощью пакета DBMS_STATS. Не уверен, что есть аналогичный механизм, чтобы обратить это вспять.

create or replace function stats_raw_to_date (p_in raw) return date is
  v_date date;
  v_char varchar2(25);
begin
  dbms_stats.CONVERT_RAW_VALUE(p_in, v_date);
  return v_date;
exception
  when others then return null;
end;
/

select stats_raw_to_date(utl_raw.cast_to_raw(
          chr(120)||chr(110)||chr(11)||chr(18)||chr(13)||chr(0)||chr(16)))
from dual;
...