SQL: Возможно ли в SUM () поля типа INTERVAL? - PullRequest
5 голосов
/ 28 июля 2010

Я пытаюсь суммировать ИНТЕРВАЛ. Э.Г.

SELECT SUM(TIMESTAMP1 - TIMESTAMP2) FROM DUAL

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

Редактировать: изменено ДАТА на ИНТЕРВАЛ

Ответы [ 7 ]

6 голосов
/ 28 июля 2010

Боюсь, вам не повезет с решением, которое работает как в 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>

Чуть больше суток, что мы и ожидали.


«Редактировать: изменено ДАТА на ИНТЕРВАЛ»

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

В следующем примере. T42T совпадает с T42, только столбцы имеют TIMESTAMP, а не DATE для своего типа данных. Запрос извлекает различные компоненты DS INTERVAL и преобразует их в секунды, которые затем суммируются и преобразуются обратно в INTERVAL:

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>

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

5 голосов
/ 28 июля 2010

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

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

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

3 голосов
/ 28 июля 2010

В SQL Server, если все ваши временные промежутки меньше 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

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

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

3 голосов
/ 28 июля 2010

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

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

0 голосов
/ 18 ноября 2013

Вы можете написать собственную агрегатную функцию :-). Пожалуйста, прочитайте внимательно 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, если хотите.

0 голосов
/ 11 августа 2013

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

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 голосов
/ 28 июля 2010

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

...