Если вы вычесть два значения типа данных DATE
, вы получите NUMBER
, представляющее количество (дробных) дней между двумя значениями. Если вы вычтете два значения типа данных TIMESTAMP
, вы получите тип данных INTERVAL
.
Таким образом, ваш ответ может быть просто:
declare
start_time_ DATE := SYSDATE- 1/3;
exect_time_ NUMBER;
begin
exect_time_ := ( sysdate - start_time_ ) *86400;
dbms_output.put_line( exect_time_ );
end;
/
Какие выходные данные: 28800
Ваша проблема в том, что вы умножаете на 86400
внутри функции EXTRACT
, а не снаружи;поэтому SYSDATE - start_time_
дает INTERVAL '8' HOUR
, а затем вы умножаете на 86400
, а INTERVAL '8' HOUR * 86400
дает значение INTERVAL '28800' DAY
, которое не соответствует точности по умолчанию для типа данных INTERVAL DAY TO SECOND
(и дает вамнеправильный ответ в любом случае).
Что бы вы хотели (если вы действительно хотите использовать TIMESTAMP
s):
declare
start_time_ TIMESTAMP := SYSTIMESTAMP - 1/3;
difference INTERVAL DAY TO SECOND := SYSTIMESTAMP - start_time_;
exect_time_ NUMBER;
begin
exect_time_ := EXTRACT( DAY FROM difference ) * 24 * 60 * 60
+ EXTRACT( HOUR FROM difference ) * 60 * 60
+ EXTRACT( MINUTE FROM difference ) * 60
+ EXTRACT( SECOND FROM difference );
dbms_output.put_line( exect_time_ );
end;
/
, который выдает что-то вроде 28800.246382
(так как естьдоля секунды между двумя SYSTIMESTAMP
вызовами).
или, если вам не нужны дробные секунды, то:
declare
start_time_ TIMESTAMP := SYSTIMESTAMP- 1/3;
exect_time_ NUMBER;
begin
exect_time_ := ( SYSDATE - CAST( start_time_ AS DATE ) ) * 86400;
dbms_output.put_line( exect_time_ );
end;
/
Какие выходные данные 28800
.
db <> fiddle
я не понимаю, почему он выдает ошибку
Это странная ошибка;код ниже тестирует различные случаи:
DECLARE
TYPE test_case IS RECORD(
units VARCHAR2(20),
difference INTERVAL DAY TO SECOND,
multiplier NUMBER(8,0)
);
TYPE test_case_list IS TABLE OF test_case;
FUNCTION createTestCase(
units VARCHAR2,
difference INTERVAL DAY TO SECOND,
multiplier NUMBER
) RETURN test_case;
test_cases test_case_list := test_case_list(
createTestCase( 'SECOND', INTERVAL '1' SECOND, 24 * 60 * 60 ),
createTestCase( 'SECOND PLUS A LITTLE', INTERVAL '1' SECOND + INTERVAL '0.001' SECOND, 24 * 60 * 60 ),
createTestCase( 'MINUTE', INTERVAL '1' MINUTE, 24 * 60 ),
createTestCase( 'MINUTE PLUS A LITTLE', INTERVAL '1' MINUTE + INTERVAL '0.001' SECOND, 24 * 60 ),
createTestCase( 'HOUR', INTERVAL '1' HOUR, 24 ),
createTestCase( 'HOUR PLUS A LITTLE', INTERVAL '1' HOUR + INTERVAL '0.001' SECOND, 24 ),
createTestCase( 'DAY', INTERVAL '1' DAY, 1 ),
createTestCase( 'DAY PLUS A LITTLE', INTERVAL '1' DAY + INTERVAL '0.001' SECOND, 1 )
);
FUNCTION createTestCase(
units VARCHAR2,
difference INTERVAL DAY TO SECOND,
multiplier NUMBER
) RETURN test_case
IS
tc test_case;
BEGIN
tc.units := units;
tc.difference := difference;
tc.multiplier := multiplier;
RETURN tc;
END;
BEGIN
FOR i IN 1 .. test_cases.COUNT LOOP
BEGIN
DBMS_OUTPUT.PUT_LINE( test_cases(i).units );
DBMS_OUTPUT.PUT_LINE( test_cases(i).difference * test_cases(i).multiplier * 100000 );
DBMS_OUTPUT.PUT_LINE( (SYSTIMESTAMP + test_cases(i).difference - SYSTIMESTAMP) * test_cases(i).multiplier * 10000 );
DBMS_OUTPUT.PUT_LINE( (SYSTIMESTAMP + test_cases(i).difference - SYSTIMESTAMP) * test_cases(i).multiplier * 100000 );
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE( SQLERRM );
END;
END LOOP;
END;
/
и выходы:
SECOND
+000100000 00:00:00.000000000
+000009999 21:21:36.000000000
ORA-01873: the leading precision of the interval is too small
SECOND PLUS A LITTLE
+000100100 00:00:00.000000000
+000010009 22:33:36.000000000
+000100099 19:12:00.000000000
MINUTE
+000100000 00:00:00.000000000
+000009999 23:59:31.200000000
ORA-01873: the leading precision of the interval is too small
MINUTE PLUS A LITTLE
+000100001 16:00:00.000000000
+000010000 03:59:02.400000000
+000100001 15:55:12.000000000
HOUR
+000100000 00:00:00.000000000
+000009999 23:59:59.280000000
ORA-01873: the leading precision of the interval is too small
HOUR PLUS A LITTLE
+000100000 00:40:00.000000000
+000010000 00:03:59.040000000
+000100000 00:39:55.200000000
DAY
+000100000 00:00:00.000000000
+000009999 23:59:59.970000000
ORA-01873: the leading precision of the interval is too small
DAY PLUS A LITTLE
+000100000 00:01:40.000000000
+000010000 00:00:09.960000000
+000100000 00:01:39.800000000
db <> Fiddle здесь
- Использование
INTERVAL
непосредственно в каждом тестовом примере работает. - Когда вы заставляете механизм PL / SQL работать
SYSTIMESTAMP + an_interval - SYSTIMESTAMP
, тогда он должен вызывать функцию SYSTIMESTAMP
дважды (что означает, что между значениями есть несколько долей секунды), и тестовые случаи не пройдены. - Когда вы добавляете небольшое количество времени к интервалу, тогда все тестовые примеры снова проходят, предполагая, чтоИнтервал с другой точностью был получен в результате расчета. (Это не предназначено для хакерского решения, просто интересное замечание).
- db <> fiddle показывает, что это происходит только в области PL / SQL;если вы выполняете те же операторы в операторах SQL, то исключений нет.
Возможно, есть ошибка, но она потребует погружения в точность типов данных, возвращаемых при динамическом генерировании интервалов из SYSTIMESTAMP
выяснить, как именно это происходит;и, помимо возможности сделать отчет об ошибке в Oracle (который они могут исправить в более поздней версии), он не сделает ваше решение более жизнеспособным.
Однако это имеет отношение к решению;не умножайте свои INTERVAL
на 86400
;вы должны использовать EXTRACT
несколько раз с DAY
, HOUR
, MINUTE
и SECOND
соответствующими аргументами и преобразовывать возвращаемые значения каждого в секунды и добавлять их или, альтернативно, использовать CAST
для обратного преобразованияиспользовать DATE
арифметику.