2010-07-28 2 views
5

Я пытаюсь суммировать INTERVAL. Например.SQL: Возможно ли использовать поля SUM() типа INTERVAL?

SELECT SUM(TIMESTAMP1 - TIMESTAMP2) FROM DUAL

Можно ли написать запрос, который будет работать как на Oracle и SQL Server? Если да, то как?

Edit: изменен ДАТА интервальных

+0

Я так не считаю. Во-первых, у них есть какой-то компонент даты, поэтому они фактически не имеют значений, если 1 и 2 минуты. Они имеют значения, как 1 минута после полуночи 26 июля. Во-вторых, что бы это означало для суммирования дат? Что вы на самом деле пытаетесь достичь? – MJB

+0

Черт, я думал, что пытаюсь суммировать даты. Фактически, я сделал это вычитание двух дат, а затем попытался SUM. Итак, у меня есть ошибка «ORA-00932: непоследовательные типы данных: ожидается, что NUMBER получил INTERVAL DAY TO SECOND». Теперь, где кнопка редактирования? :) –

+0

Некоторые СУБД поддерживают суммирование значений INTERVAL - один из них - IBM Informix Dynamic Server. Тем не менее, большинство нет. –

ответ

5

Хорошо, после некоторого ада, с помощью ответов stackoverflowers я нашел решение, которое соответствует моим потребностям.


SELECT 
    SUM(CAST((DATE1 + 0) - (DATE2 + 0) AS FLOAT) AS SUM_TURNAROUND 
FROM MY_BEAUTIFUL_TABLE 
GROUP BY YOUR_CHOSEN_COLUMN 

Это возвращает поплавок (который полностью подходит для меня), который представляет дни как на сервере Oracle ant SQL Server.

Причина, по которой я добавил ноль к обеим DATE, заключается в том, что в моих столбцах даты в Oracle DB есть тип TIMESTAMP, а на SQL Server - тип DATETIME (что явно странно). Таким образом, добавление нуля в TIMESTAMP для Oracle работает так же, как и кастинг на сегодняшний день, и это не влияет на тип DATETIME SQL Server.

Спасибо, ребята! Вы были очень полезны.

+1

Как это ответ, который точно и полностью решает ваш вопрос, вы должны его принять. – APC

+0

Я буду! ..завтра. –

3

Вы не можете суммировать два DateTimes. Это не имеет смысла - то есть, что равно 15:00:00 плюс 23:59:00? Некоторое время на следующий день? и т. д.

Но вы можете добавить инкремент времени с помощью функции типа Dateadd() в SQL Server.

0

Я тоже не думаю, что это возможно. Пойдите с пользовательскими решениями, которые вычисляют значение даты в соответствии с вашими предпочтениями.

3

В SQL Server до тех пор, пока ваши индивидуальные timespans все менее 24 часов вы можете сделать что-то вроде

WITH TIMES AS 
(
SELECT CAST('01:01:00' AS DATETIME) AS TimeSpan 
UNION ALL 
SELECT '00:02:00' 
UNION ALL 
SELECT '23:02:00' 
UNION ALL 
SELECT '17:02:00' 
--UNION ALL SELECT '24:02:00' /*This line would fail!*/ 
), 
SummedTimes As 
(
SELECT cast(SUM(CAST(TimeSpan AS FLOAT)) as datetime) AS [Summed] FROM TIMES 
) 
SELECT 
    FLOOR(CAST(Summed AS FLOAT)) AS D, 
    DATEPART(HOUR,[Summed]) AS H, 
    DATEPART(MINUTE,[Summed]) AS M, 
    DATEPART(SECOND,[Summed]) AS S 
FROM SummedTimes 

дает

D   H   M   S 
----------- ----------- ----------- ----------- 
1   17   7   0 

Если вы хотите, чтобы справиться с timespans больше, чем 24 часа я подумайте, что вам нужно будет взглянуть на интеграцию CLR и структуру TimeSpan. Определенно не переносимый!

Edit: SQL Server 2008 имеет DateTimeOffset тип данных, которые могли бы помочь, но это не позволяет ни SUM мина или быть брошенным, чтобы плавать

6

Я боюсь, что вы будете не повезло с решение, которое работает как в Oracle, так и в MSSQL. Арифметика даты - это то, что сильно отличается от различных вариантов СУБД.

В любом случае, в Oracle мы можем использовать даты в простой арифметике. И у нас есть функция NUMTODSINTERVAL, которая превращает число в ДЕНЬ ВТОРОГО ИНТЕРВАЛА. Итак, давайте соединим их.

Простые тестовые данные, две строки с парами дат неровные двенадцать часов друг от друга:

SQL> alter session set nls_date_format = 'dd-mon-yyyy hh24:mi:ss' 
    2/

Session altered. 

SQL> select * from t42 
    2/

D1     D2 
-------------------- -------------------- 
27-jul-2010 12:10:26 27-jul-2010 00:00:00 
28-jul-2010 12:10:39 28-jul-2010 00:00:00 

SQL> 

Простой SQL запрос, чтобы найти сумму затраченного времени:

SQL> select numtodsinterval(sum(d1-d2), 'DAY') 
    2 from t42 
    3/

NUMTODSINTERVAL(SUM(D1-D2),'DAY') 
----------------------------------------------------- 
+000000001 00:21:04.999999999 

SQL> 

Только за один день, который это то, чего мы ожидаем.


«Edit: изменен ДАТА интервальных»

Работа с TIMESTAMP колоннами немного больше трудоемкий, но мы все еще можем работать тот же трюк.

В следующем примере. T42T - это то же самое, что и T42, только для столбцов вместо DATE используется TIMESTAMP, а не тип DATAT.Запроса извлекает различные компоненты DS ИНТЕРВАЛА и преобразует их в секунды, которые затем суммируются и преобразуются обратно в интервальный:

SQL> select numtodsinterval(
    2    sum(
    3     extract (day from (t1-t2)) * 86400 
    4     + extract (hour from (t1-t2)) * 3600 
    5     + extract (minute from (t1-t2)) * 600 
    6     + extract (second from (t1-t2)) 
    7   ), 'SECOND') 
    8 from t42t 
    9/

NUMTODSINTERVAL(SUM(EXTRACT(DAYFROM(T1-T2))*86400+EXTRACT(HOURFROM(T1-T2))* 
--------------------------------------------------------------------------- 
+000000001 03:21:05.000000000 

SQL> 

По крайней мере, этот результат в круглых секундах!

+0

Это просто дало мне блестящую идею, почему моя функция SUM не удалась. Поскольку я использовал TIMESTAMP вместо DATETIME. Арифметические операции с TIMESTAMP производят тип INTERVAL, функция SUM не может рассчитывать на Oracle. Поскольку кастинг от TIMESTAMP до DATETIME слишком просто, моя проблема решена на Oracle. Для обеспечения устойчивости я, вероятно, буду использовать функцию пользователя для выполнения грязной работы для меня. –

+0

Для меня, если я ставлю min * 600, это становится неправильным .. т.е. '0 5: 1: 5.793000000', считайте 5 часов и 10 минут, вместо этого должно быть 5 часов и 1 мин. Поэтому я заменил его на min * 60 и работал. – NaaN

0

Вы также можете использовать это:

select 
    EXTRACT (DAY FROM call_end_Date - call_start_Date)*86400 + 
    EXTRACT (HOUR FROM call_end_Date - call_start_Date)*3600 + 
    EXTRACT (MINUTE FROM call_end_Date - call_start_Date)*60 + 
    extract (second FROM call_end_Date - call_start_Date) as interval 
from table; 
0

Вы можете написать у вас есть функция совокупного :-). Пожалуйста, внимательно прочитайте http://docs.oracle.com/cd/B19306_01/appdev.102/b14289/dciaggfns.htm

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

create or replace type Sum_Interval_Obj as object 
(
    -- Object for creating and support custom aggregate function 
    duration interval day to second, -- In this property You sum all interval 

    -- Object Init 
    static function ODCIAggregateInitialize(
    actx IN OUT Sum_Interval_Obj 
    ) return number, 

    -- Iterate getting values from dataset 
    member function ODCIAggregateIterate(
    self   IN OUT Sum_Interval_Obj, 
    ad_interval IN interval day to second 
    ) return number, 

    -- Merge parallel summed data 
    member function ODCIAggregateMerge(
    self IN OUT Sum_Interval_Obj, 
    ctx2 IN Sum_Interval_Obj 
) return number, 

    -- End of query, returning summary result 
    member function ODCIAggregateTerminate 
    (
    self  IN Sum_Interval_Obj, 
    returnValue OUT interval day to second, 
    flags  IN number 
) return number 

) 
/

create or replace type body Sum_Interval_Obj is 

    -- Object Init 
    static function ODCIAggregateInitialize(
    actx IN OUT Sum_Interval_Obj 
    ) return number 
    is 
    begin 
    actx := Sum_Interval_Obj(numtodsinterval(0,'SECOND')); 
    return ODCIConst.Success; 
    end ODCIAggregateInitialize; 

    -- Iterate getting values from dataset 
    member function ODCIAggregateIterate(
    self   IN OUT Sum_Interval_Obj, 
    ad_interval IN interval day to second 
    ) return number 
    is 
    begin 
    self.duration := self.duration + ad_interval; 
    return ODCIConst.Success; 
    exception 
    when others then 
     return ODCIConst.Error; 
    end ODCIAggregateIterate; 

    -- Merge parallel calculated intervals 
    member function ODCIAggregateMerge(
    self IN OUT Sum_Interval_Obj, 
    ctx2 IN  Sum_Interval_Obj 
    ) return number 
    is 
    begin 
    self.duration := self.duration + ctx2.duration; -- Add two intervals 
    -- return = All Ok! 
    return ODCIConst.Success; 
    exception 
    when others then 
     return ODCIConst.Error; 
    end ODCIAggregateMerge; 

    -- End of query, returning summary result 
    member function ODCIAggregateTerminate(
    self  IN Sum_Interval_Obj, 
    returnValue OUT interval day to second, 
    flags  IN number 
    ) return number 
    is 
    begin 
    -- return = All Ok, too! 
    returnValue := self.duration; 
    return ODCIConst.Success; 
    end ODCIAggregateTerminate; 

end; 
/

-- You own new aggregate function: 
CREATE OR REPLACE FUNCTION Sum_Interval(
    a_Interval interval day to second 
    ) RETURN interval day to second 
    PARALLEL_ENABLE AGGREGATE USING Sum_Interval_Obj; 
/

Последнее, проверить вашу функцию:

select sum_interval(duration) 
    from (select numtodsinterval(1,'SECOND') as duration from dual union all 
     select numtodsinterval(1,'MINUTE') as duration from dual union all 
     select numtodsinterval(1,'HOUR') as duration from dual union all 
     select numtodsinterval(1,'DAY')  as duration from dual); 

Наконец Вы может создать функцию SUM, если хотите.

Смежные вопросы