T-SQL: округление до ближайшего 15-минутного интервала - PullRequest
42 голосов
/ 06 мая 2009

Какой лучший способ округлить значение ЧЧ: ММ до ближайшего 15-минутного интервала? Я не отслеживаю секунды, чтобы они не имели значения.

00:08:00 becomes 00:15:00 
00:07:00 becomes 00:00:00 
01:59:00 becomes 02:00:00 

и так далее. Есть ли для этого элегантный метод без UDF или оператора Case?

РЕДАКТИРОВАТЬ: Вот SQL, который я использую, чтобы получить значения выше, которые я хотел бы округлить:

CONVERT(CHAR(8), DATEADD(n, SUM(DATEDIFF(n, starttime, stoptime)), 0), 108)

starttime и stoptime - это SQL datetime с.

Ответы [ 15 ]

63 голосов
/ 26 октября 2009

В настоящее время я использую вариант dateadd / datediff с нулевой (0) датой для этого. Кастинг не требуется:

select dateadd(minute, datediff(minute,0,GETDATE()) / 15 * 15, 0)

GETDATE () - это любое ваше время.

Это сработает для дат, по крайней мере, до 5500 года до того, как датировка перестанет работать из-за переполнения. Однако, если вы попытаетесь использовать вторую точность, выше сразу не получится.

Использование другой фиксированной даты, такой как '2009-01-01' или сегодняшняя дата (предупреждение, более уродливый SQL), исправит это. Будущая дата также будет работать. Пока он имеет временную часть 00:00:00, вы можете использовать другую дату и время.

например: округление до ближайших 30 секунд:

select dateadd(second, round(datediff(second, '2010-01-01', GETDATE()) / 30.0, 0) * 30, '2010-01-01');
27 голосов
/ 06 мая 2009

Здесь ответили Как округлить время в T-SQL , и я думаю, что это должно работать для вас.

CREATE FUNCTION [dbo].[RoundTime] (@Time datetime, @RoundTo float) RETURNS datetime
AS
BEGIN
    DECLARE @RoundedTime smalldatetime, @Multiplier float

    SET @Multiplier = 24.0 / @RoundTo

    SET @RoundedTime= ROUND(CAST(CAST(CONVERT(varchar, @Time, 121) AS datetime) AS float) * @Multiplier, 0) / @Multiplier

    RETURN @RoundedTime
END

-- Usage    
SELECT dbo.RoundTime('13:15', 0.5)
20 голосов
/ 29 июня 2016

Я знаю, что это старый пост, но хотел бы поделиться своим ответом. Это основано на ответе @hbrowser. Вот что я придумала. Это округляется вверх или вниз до ближайших 15 минут.

SELECT DATEADD(MINUTE, ROUND(DATEDIFF(MINUTE, 0, GETDATE()) / 15.0, 0) * 15, 0);

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

Вы можете изменить способ округления, переключив функцию ROUND на FLOOR или CAST expr AS INT, чтобы всегда округлять, или используйте CEILING, чтобы всегда округлять.

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

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

ПРИМЕЧАНИЕ: чтобы упростить вывод, каждый результат был приведен к ВРЕМЕНИ (0), это делается только для упрощения вывода для этого конкретного примера.

DECLARE @SequenceStart SmallDateTime = CAST(GETDATE() AS Date); 
DECLARE @SequenceEnd SmallDateTime = DateAdd(HOUR, 2, @SequenceStart); -- Recursive CTEs should always have an upper limit
DECLARE @SequenceIntMins INT = 5; -- increment by 5 to show the difference with rounding
WITH TimeSequence([Time]) as
(
    SELECT @SequenceStart as [Time]
    UNION ALL
    SELECT DateAdd(MINUTE, 5, [Time]) FROM TimeSequence 
    WHERE [Time] <= @SequenceEnd
)
    SELECT [Time] = Cast([Time] as TIME(0))
    , Rounded = CAST(DATEADD(MINUTE, ROUND(DATEDIFF(MINUTE, 0, [Time]) / 15.0, 0) * 15, 0) as TIME(0))
    , Casted = CAST(DATEADD(MINUTE, CAST(DATEDIFF(MINUTE, 0, [Time]) / 15.0 AS INT) * 15, 0) as TIME(0))
    , Floored = CAST(DATEADD(MINUTE, FLOOR(DATEDIFF(MINUTE, 0, [Time]) / 15.0) * 15, 0) as TIME(0))
    , Ceilinged = CAST(DATEADD(MINUTE, CEILING(DATEDIFF(MINUTE, 0, [Time]) / 15.0) * 15, 0) as TIME(0))
FROM TimeSequence OPTION ( MaxRecursion 1000);
-- MaxRecursion may be neccessary if you change the interval or end of the sequence
Time        Rounded     Casted      Floored     Ceilinged
00:00:00    00:00:00    00:00:00    00:00:00    00:00:00
00:05:00    00:00:00    00:00:00    00:00:00    00:15:00
00:10:00    00:15:00    00:00:00    00:00:00    00:15:00
00:15:00    00:15:00    00:15:00    00:15:00    00:15:00
00:20:00    00:15:00    00:15:00    00:15:00    00:30:00
00:25:00    00:30:00    00:15:00    00:15:00    00:30:00
00:30:00    00:30:00    00:30:00    00:30:00    00:30:00
00:35:00    00:30:00    00:30:00    00:30:00    00:45:00
00:40:00    00:45:00    00:30:00    00:30:00    00:45:00
00:45:00    00:45:00    00:45:00    00:45:00    00:45:00
00:50:00    00:45:00    00:45:00    00:45:00    01:00:00
00:55:00    01:00:00    00:45:00    00:45:00    01:00:00
01:00:00    01:00:00    01:00:00    01:00:00    01:00:00
01:05:00    01:00:00    01:00:00    01:00:00    01:15:00
6 голосов
/ 06 мая 2009

Вы можете округлить дату до ближайшего квартала, например:

cast(floor(cast(getdate() as float(53))*24*4)/(24*4) as datetime)

Приведение даты-времени к двойному предшествованию, чтобы избежать переполнения, double = float (53). Умножьте на 24 * 4 количество кварталов за день. Округлите до ближайшего кратного четверти с помощью floor (), а затем разделите на 24 * 4, чтобы преобразовать его в обычное время.

5 голосов
/ 10 сентября 2009

Попробовал ответ Андомара, в 30 и 00 возникли проблемы с округлением - так что несколько настроек, и это прекрасно работает:

cast(round(floor(cast(getdate() as float(53))*24*4)/(24*4),5) as smalldatetime)

Это покажет последние 15-минутные приращения, а не ближайшие, т. Е. Оно не пойдет вперед, что именно то, что мне нужно.

2 голосов
/ 25 октября 2013

Хорошо, самый простой способ:

преобразовать минуты в десятичное число, разделив на 60.

8/60 = 0.1333333333333333

умножить на 4

0.1333333333333333 * 4   = 0.5333333333333333

Вокруг изделия:

Round(0.5333333333333333,0) = 1

разделите число раунда на 4

1/4 = 0.25 = 15 minutes

если вы хотите, чтобы минуты просто умножили на 60

0.25*60 = 15

Дайте человеку рыбу ....

1 голос
/ 26 июня 2015

- это мой любимый способ округлить время

DECLARE @Time DATETIME = GETDATE()
       ,@RoundInterval INT = 30  --in minutes, needs to be a number that can be divided evenly into 60
       ,@RoundDirection INT = 2  --0 is down to the last interval, 1 is to the nearest interval, 2 is up to the next interval

SELECT  DATEADD(MINUTE,DATEDIFF(MINUTE,0,DATEADD(SECOND,30*@RoundDirection*@RoundInterval,@Time))/@RoundInterval*@RoundInterval,0)
1 голос
/ 06 июня 2013
DECLARE @t time  ='00:51:00.000' 
DECLARE @m  int = DATEPART(MI,@t)%15

-- 2008
SELECT DATEADD(mi,CASE WHEN @m >=8 THEN 15-@m ELSE -1*@m END,@t)

-- 2012
SELECT DATEADD(mi,IIF(@m >=8,15-@m,-1*@m),@t)
1 голос
/ 07 декабря 2009

Попробуйте это:

Declare @Dt DateTime 
Set @Dt = getDate()

Select DateAdd(minute, 
        15 * ((60 * Datepart(hour, @Dt) + 
        Datepart(Minute, @Dt)+ 
        Case When DatePart(second, @Dt) < 30 
        Then 7 Else 8 End) / 15),
    DateAdd(day, DateDiff(day, 0, @Dt), 0))
0 голосов
/ 05 апреля 2017

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

Простая функция:

    ALTER FUNCTION [dbo].[RoundOffDateTime]
(
    @IncDate    DATETIME,
    @Increment  INT
)
RETURNS SMALLDATETIME
AS
BEGIN

    DECLARE @IncrementPercent DECIMAL(2,2) = CAST(@Increment as decimal)/60
    DECLARE @IncMinutes REAL = ROUND(CAST(DATEPART(mi,@IncDate) as decimal)/CAST(@Increment as decimal),0)
    DECLARE @MinutesNeeded INT = CAST(@IncMinutes * @Increment as INT)

    RETURN CAST(DATEADD(mi,@MinutesNeeded,DATEADD(ss,-DATEPART(ss,@IncDate),DATEADD(mi,-DATEPART(mi,@IncDate),@IncDate))) as smalldatetime)

END
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...