Как определить недопустимые (поврежденные) значения, хранящиеся в столбцах Oracle DATE

Oracle 10.2.0.5

Каков самый простой способ определить строки в таблице с "недопустимыми" значениями в столбцах DATE. Под "недействительным" здесь я имею в виду двоичное представление, которое нарушает правила Oracle для значений даты.

Недавно у меня возникла проблема с недопустимой датой, хранящейся в столбце.

Я смог использовать предикат запроса для поиска конкретной проблемной строки:

  WHERE TO_CHAR(date_expr,'YYYYMMDDHH24MISS') = '00000000000000'

В случае, когда у меня был, байт в столетии был недействительным...

 select dump(h.bid_close_date) from mytable h where h.id = 54321

 Typ=12 Len=7: 220,111,11,2,1,1,1

Бакт века должен быть 100 + двухзначный век. В этом случае было добавлено дополнительно 100, как будто значение столетия было "120", что сделало год "12011". (Единственный способ, которым я знаю, чтобы получить недопустимые значения DATE в базе данных, использует OCI, используя собственное 7-байтовое представление DATE.)

В этом случае функция TO_CHAR вернула идентифицируемую строку, которую я мог бы использовать для определения значения дрожания DATE.

Мой вопрос: существует ли более общий или более простой подход (предпочтительно с использованием оператора SQL SELECT) для идентификации строк с "недопустимыми" значениями в столбцах DATE.

Ответ 1

Это указывает недопустимые месяцы

SELECT rowid,
       pk_column,
       DUMP(date_column, 1010) AS dump1
FROM   table
WHERE  TO_NUMBER(SUBSTR(DUMP(date_column, 1010), INSTR(DUMP( date_column, 1010),
                                              ',', 1, 2
                                                     ) + 1,
                                  INSTR(DUMP(date_column, 1010), ',', 1, 3) - (
                                  INSTR(DUMP( date_column, 1010), ',', 1, 2) + 1
                                  ))) = 0; 

Обновление с использованием того же предложения where, я обнаружил, что в этих случаях число месяца было равным нулю.

Ответ 2

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

Что-то вроде этого:

create or replace function is_a_date 
    ( p_date in date )
    return varchar2
is
    d date;
begin
    d := to_date(to_char(p_date,  'SYYYYMMDDHH24MISS'),  'SYYYYMMDDHH24MISS') ;
    if d != p_date then
        return 'not a proper date';
    else
        return 'good date';
    end if;
exception
    when others  then
        return 'not a date';
end;
/ 

Это преобразует дату в строку и обратно. Он ловит исключения, брошенные датой. Если конечный продукт не совпадает с датой ввода, то, по-видимому, что-то заблудилось в переводе; честно говоря, я не уверен, что дата 12011 будет успешно перенесена в строку, так что это подход с поясом. Это немного сложно записать эту утилиту без каких-либо тестовых данных!

В этом запросе будут указаны все недействительные даты:

 select h.id, dump(h.bid_close_date)
 from mytable h 
 where h.bid_close_date is not null
 and is_a_date(h.bid_close_date) != 'good date';

Ответ 3

Без добавления функции простой предикат

TO_CHAR(date_col,'YYYYMMDDHH24MISS') = '000000000000'

представляется удовлетворительным для идентификации поврежденных значений, хранящихся в столбце Oracle DATE. Добавление функции оказывается ненужным. Проверка на поврежденные даты должна выполняться в инструкции SQL SELECT и не требовать от пользователя наличия привилегии CREATE FUNCTION в базе данных.

Ответ 4

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

Однако спросите себя, действительно ли у вас дата 1066-10-14? Это юридическая ценность, но вы, вероятно, не имеете счетов, напечатанных в этот день, например. Таким образом, вы можете опрокинуть неверную проверку даты в более крупную проблему того, что вы считаете действительным в контексте вашего приложения.

Ответ 5

У меня был SQL Error: ORA-01841: (full) year must be between -4713 and +9999, and not be 0 01841. 00000 - "(full) year must be between -4713 and +9999, and not be 0".

Чтобы определить строки с неправильной датой, я сделал следующее.

declare
    cursor mydates is select table_pk, your_date_col from table;
    c_date table.your_date_col%type;
    c_pk table.table_pk%type;
    testrow table.your_date_col%type;
begin
    open mydates;
    loop
    begin
        fetch mydates into c_pk, c_date;
        exit when mydates%notfound;
        testrow := TO_TIMESTAMP(c_date,'YYYY-MM-DD HH24:MI:SS');
    exception when others then
        dbms_output.put_line('bad file: ' || c_pk);
    end;
    end loop;
    close mydates;
end;

Итак, все, что я сделал, это создал курсор, перебрал элементы и проверил каждый из них и отобразил идентификатор, чтобы я мог легко найти плохие строки.