Переместить миллисекунды на одно значение позиции

в результате "неправильной" даты синтаксического анализа java (моя ошибка) Теперь у меня есть несколько тысяч записей в Oracle DB с неправильной отметкой времени. Проблема заключается в следующем:

Временная метка 2018-06-26 11:15:43.950 была вставлена ​​в БД как 26-FEB-18 15.11.43.095000000 AM

Есть ли функция для сужения миллисекунд? Я только понял, что с некоторыми функциями to_char , to_date в сочетании с подстрокой я могу «удалить» 0 перед миллисекундами, но мне кажется, что это недостаточно хорошее разрешение.

Заранее спасибо!

РЕДАКТИРОВАТЬ: К сожалению, я не могу повторно загрузить данные с исправленным алгоритмом.


person Alone89    schedule 26.02.2018    source источник


Ответы (3)


Лучший вариант: перезагрузить данные из оригинального источника, после того, как вы исправите свой код.

Если у вас больше нет доступа к исходным данным, и вы должны исправить все это на месте, используйте инструкцию UPDATE ниже (показана в контексте):

create table tbl ( ts timestamp );

insert into tbl ( ts ) values ( timestamp '2018-06-26 11:15:43.0950' );
commit;

select ts from tbl;

TS                                     
----------------------------------------
2018-06-26 11.15.43.095000000         

update tbl set ts = ts + 9 * (ts - cast(ts as timestamp(0)));

1 row updated.

commit;

select ts from tbl;

TS                                     
----------------------------------------

2018-06-26 11.15.43.950000000  

Объяснение:

Если исходная временная метка имела форму X + w, где X — это целые секунды, а w — дробная часть секунды, текущее значение равно X + z, где z = w/10. (Вы добавили ошибочную цифру 0 сразу после запятой, что означает, что вы разделили w на десять). Итак: сейчас у вас X + z, но вы хотите X + w, или, другими словами, X + 10 * z. Итак, вы должны добавить 9 * z к тому, что у вас уже есть.

Чтобы получить z (дробная часть метки времени), вам нужно вычесть X (целая часть) из метки времени. X само по себе является усечением метки времени до целых секунд. Нет функции TRUNC() для усечения до целых секунд, но функция CAST до TIMESTAMP(0) делает эту работу достаточно хорошо.

Чтобы использовать ваши образцы данных: X — это временная метка '2018-06-26 11:15:43.000000'. Это также результат cast(ts as timestamp(0)). w — это .9500, а z — это то, что попало в вашу таблицу, .0950. Ваше текущее значение X + z, но вы хотите X + w. Это X + 10 * z = (X + z) + 9 * z, а теперь помните, что (X + z) — это всего лишь ts (значение, которое вы имеете в таблице на данный момент), поэтому вам нужно всего лишь добавить девять раз значение z, которое составляет разницу (ts - X).

person mathguy    schedule 26.02.2018
comment
Вау, просто вау :) Несмотря на то, что я прочитал ваше объяснение несколько раз, я все еще не понимаю, как это работает, но раздел обновлений работает безупречно :) - person Alone89; 26.02.2018
comment
@ Alone89 - Хорошо, я добавил абзац, показывающий, кто X, w и z в конкретном примере из вашего вопроса (и из моей демонстрации). - person mathguy; 26.02.2018
comment
Аааа, теперь я понял :) Теперь я понимаю, почему ваш ник mathguy, поскольку это решение никогда не приходило мне в голову ни в одном сценарии: D Большое спасибо. - person Alone89; 26.02.2018

Вы можете использовать TRUNC( time, 'MI' ), чтобы получить время без компонента секунд (как тип данных Date, который вы можете затем использовать CAST для преобразования обратно в метку времени). Затем вы можете использовать EXTRACT( SECOND from time ), чтобы получить компонент секунд (включая дробные секунды) и разделить его на компоненты секунд и миллисекунд (последний вы можете просто умножить на 10, чтобы выполнить коррекцию), а затем использовать NUMTODSINTERVAL для преобразования числовых значений в интервал который можно добавить к усеченной метке времени.

Как это:

скрипт SQL

Настройка схемы Oracle 11g R2:

CREATE TABLE times ( time ) AS
SELECT TIMESTAMP '2018-06-26 11:15:43.095' FROM DUAL;

Запрос 1:

SELECT time,
       CAST( TRUNC( time, 'MI' ) AS TIMESTAMP )
       + NUMTODSINTERVAL(
           TRUNC( EXTRACT( SECOND FROM time ) )
           + MOD( EXTRACT( SECOND FROM time ), 1 ) * 10,
           'SECOND'
         ) As updated_time
FROM   times

Результаты:

|                    TIME |           UPDATED_TIME |
|-------------------------|------------------------|
| 2018-06-26 11:15:43.095 | 2018-06-26 11:15:43.95 |

Однако вам может быть лучше просто снова загрузить временные метки из исходных данных (если вы можете) с исправленным алгоритмом, поскольку вы потеряете наименее значащую цифру из миллисекунд.

person MT0    schedule 26.02.2018
comment
Вау, превосходное спасибо - это ответ, который я искал. Также спасибо за ссылку на скрипку sql :) - person Alone89; 26.02.2018

К сожалению, ваши данные навсегда повреждены. Невозможно отличить смещенные временные метки от правильных:

2018-06-26 11:15:43.950 
2018-06-26 11:15:43.095 

оба приводят к следующей отметке времени

26-FEB-18 11.15.43.095000000 AM
person Stavr00    schedule 26.02.2018
comment
Да, с точки зрения применения, это правда. К счастью, у меня есть много данных, зарегистрированных в БД, и я могу отличить поврежденный, просто сравнив временную метку в ответе журнала (которая, если она повреждена, меньше) и временную метку в запросе журнала) - person Alone89; 26.02.2018
comment
Это важно, потому что иначе было бы невозможно найти, какие строки нуждаются в исправлении. - person Stavr00; 26.02.2018