Как сгруппировать время по часам или по 10 минут

167

например, когда я делаю

SELECT [Date]
  FROM [FRIIB].[dbo].[ArchiveAnalog]
  GROUP BY [Date]

как я могу указать групповой период?

MS SQL 2008

2-й править

я стараюсь

SELECT MIN([Date]) AS RecT, AVG(Value)
  FROM [FRIIB].[dbo].[ArchiveAnalog]
  GROUP BY (DATEPART(MINUTE, [Date]) / 10)
  ORDER BY RecT

изменил% 10 на / 10. можно ли сделать вывод даты без миллисекунд?

CND
источник

Ответы:

213

наконец сделано с

GROUP BY
DATEPART(YEAR, DT.[Date]),
DATEPART(MONTH, DT.[Date]),
DATEPART(DAY, DT.[Date]),
DATEPART(HOUR, DT.[Date]),
(DATEPART(MINUTE, DT.[Date]) / 10)
CND
источник
11
Я сделал это ROUND((DATEPART(MINUTE, DT.[Date]) / 5),0,1) * 5так, чтобы, когда я смотрю на данные, они коррелировали с ближайшим временным интервалом
hdost
7
Год, месяц и день можно упростить до DATE(DT.[Date]).
@ Килан не работает для меня - однако CONVERT (дата, DT. [Дата]) работает.
Дэн Парсонсон
datepart(hour, workingPolicy.workingHours)/2.0дает 1.5пока datepart(hour, '1900-01-01 09:00:30.000')/2.0дает 4.5, я не понимаю почему? Примечание: workingPolicy.workingHours = 1900-01-01 09: 00: 30.000 . пожалуйста, помогите
affanBajwa
65

Я супер опоздал на вечеринку, но это не отражено ни в одном из существующих ответов:

GROUP BY DATEADD(MINUTE, DATEDIFF(MINUTE, '2000', date_column) / 10 * 10, '2000')
  • 10И MINUTEсроки могут быть изменены в любое число и DATEPART, соответственно.
  • Это DATETIMEзначение, которое означает:
    • Он отлично работает через длительные промежутки времени. (Между годами нет столкновения.)
    • Включение этого в SELECTоператор даст вашему выводу столбец с довольно усеченным выводом на указанном вами уровне.
  • '2000'является «якорной датой», вокруг которой SQL будет выполнять математическую обработку даты. Джереон обнаружил ниже, что вы сталкиваетесь с целочисленным переполнением с предыдущей привязкой ( 0), когда вы группируете последние даты по секундам или миллисекундам.
SELECT   DATEADD(MINUTE, DATEDIFF(MINUTE, '2000', aa.[date]) / 10 * 10, '2000')
                                                               AS [date_truncated],
         COUNT(*) AS [records_in_interval],
         AVG(aa.[value]) AS [average_value]
FROM     [friib].[dbo].[archive_analog] AS aa
GROUP BY DATEADD(MINUTE, DATEDIFF(MINUTE, '2000', aa.[date]) / 10 * 10, '2000')
ORDER BY [date_truncated]

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

  • Используйте DATEADD(DAY, DATEDIFF(DAY, 0, aa.[date]), 0)вместо того, '2000'где он появляется выше. Ваш запрос будет полностью нечитаемым, но он будет работать.

  • Альтернативой может быть CONVERT(DATETIME, CONVERT(DATE, aa.[date]))замена.

2 32 ≈ 4.29E + 9, так что если у вас DATEPARTесть SECOND, вы получите 4,3 миллиарда секунд с каждой стороны, или «якорь ± 136 лет». Аналогично, 2 32 миллисекунды - это ≈ 49,7 дня.
Если ваши данные на самом деле пролеты веков или Millenia и является до сих пор с точностью до секунды или миллисекунды ... поздравляем! Что бы вы ни делали, продолжайте делать это.

Майкл - Где Клэй Ширки
источник
было бы возможно сделать это с определенной даты начала?
Пиландер
1
@pylander Конечно. Просто поставьте whereпункт перед group by.
Майкл - Где Клей Ширки
2
Понимаю. Таким образом, изменение этого значения / 20 * 20будет таким же, как сбор данных за 20-минутные интервалы? Извините, я сейчас борюсь с математикой.
однобоко
2
для ВТОРОГО как DATEPART я получаю сообщение об ошибке ( The datediff function resulted in an overflow. The number of dateparts separating two date/time instances is too large. Try to use datediff with a less precise datepart.). Кажется, что MINUTE - самая маленькая часть даты, которую вы можете использовать с этим подходом.
19
1
@jeroenh, ты прав. Я добавил раздел, чтобы поговорить об этом. tldr: Изменение 0в '2000'(кавычки важны!) и попробуйте SECONDеще раз.
Майкл - Где Клэй Ширки
17

В T-SQL вы можете:

SELECT [Date]
  FROM [FRIIB].[dbo].[ArchiveAnalog]
  GROUP BY [Date], DATEPART(hh, [Date])

или

по минутному использованию DATEPART(mi, [Date])

или

на 10 минут использования DATEPART(mi, [Date]) / 10(как предложил Тимоти)

tzup
источник
1
GROUP BY [Дата], DATEPART (чч, [Дата]) беспорядок, не так ли?
CND
1
@nCdy Должно быть в порядке, в противном случае вы получите ошибку "... недопустимая в списке выбора, потому что она не содержится ни в
статистической
1
Как вы группируете по всей дате и по часам в одно и то же время? Я просто использую Мин Дата.
CND
если вы используете Min (Дата), то, конечно, вы можете убрать Date в Group By.
tzup
3
Минуты, разделенные на 10, создадут группировку по 10-минутным интервалам, что и нужно. По модулю 10 смысла нет.
Тар
12

За 10-минутный интервал вы бы

GROUP BY (DATEPART(MINUTE, [Date]) / 10)

Как уже упоминалось tzup и Pieter888 ... сделать часовой интервал, просто

GROUP BY DATEPART(HOUR, [Date])
Тимоти Хоури
источник
2
но здесь я группирую протокол 1980 года с протоколом 2011 года: СИ нужно заботиться об этом.
CND
% Будет правильной математикой? Разве это не создаст 10 групп. Например: 1% 10 = 9 2% 10 = 8 Это также не обязательно будет правильной хронологической группировкой. Я думаю, что нормальное разделение будет правильным. Если% не является делением остатка в sql.
Стивен
12
Минуты, разделенные на 10, создадут группировку по 10-минутным интервалам, что и нужно. По модулю 10 смысла нет.
Тар
Договорились с Tar. Группировка не имеет смысла.
MikeMurko
7

Должно быть что-то вроде

select timeslot, count(*)  
from 
    (
    select datepart('hh', date) timeslot
    FROM [FRIIB].[dbo].[ArchiveAnalog]  
    ) 
group by timeslot

(Не уверен на 100% в синтаксисе - я скорее парень из Oracle)

В Oracle:

SELECT timeslot, COUNT(*) 
FROM
(  
    SELECT to_char(l_time, 'YYYY-MM-DD hh24') timeslot 
    FROM
    (
        SELECT l_time FROM mytab  
    )  
) GROUP BY timeslot 
Фрэнк Шмитт
источник
6

Оригинальный ответ автор дал работы довольно хорошо. Просто чтобы расширить эту идею, вы можете сделать что-то вроде

group by datediff(minute, 0, [Date])/10

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

Дерек
источник
1
Каким будет запрос MySQL для вышеупомянутого?
Биранки
4

Для MySql:

GROUP BY
DATE(`your_date_field`),
HOUR(`your_date_field`),
FLOOR( MINUTE(`your_date_field`) / 10);
nobilist
источник
1

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

CREATE FUNCTION [dbo].[fn_MinuteIntervals]
    (
      @startDate SMALLDATETIME ,
      @endDate SMALLDATETIME ,
      @interval INT = 1
    )
RETURNS @returnDates TABLE
    (
      [date] SMALLDATETIME PRIMARY KEY NOT NULL
    )
AS
    BEGIN
        DECLARE @counter SMALLDATETIME
        SET @counter = @startDate
        WHILE @counter <= @endDate
            BEGIN
                INSERT INTO @returnDates VALUES ( @counter )
                SET @counter = DATEADD(n, @interval, @counter)
            END
        RETURN
    END
user3193141
источник
1
declare @interval tinyint
set @interval = 30
select dateadd(minute,(datediff(minute,0,[DateInsert])/@interval)*@interval,0), sum(Value_Transaction)
from Transactions
group by dateadd(minute,(datediff(minute,0,[DateInsert])/@interval)*@interval,0)
Родас П.Т.
источник
8
Добро пожаловать в stackoverflow. Вы должны полностью объяснить свой ответ, и что это добавляет к 10 другим ответам уже на сайте.
Simon.SA
0

Для SQL Server 2012, хотя я считаю, что он будет работать в SQL Server 2008R2, я использую следующий подход, чтобы сократить время до миллисекунды:

DATEADD(MILLISECOND, -DATEDIFF(MILLISECOND, CAST(time AS DATE), time) % @msPerSlice, time)

Это работает:

  • Получение количества миллисекунд между фиксированной точкой и целевым временем:
    @ms = DATEDIFF(MILLISECOND, CAST(time AS DATE), time)
  • Взяв остаток от деления этих миллисекунд на временные интервалы:
    @rms = @ms % @msPerSlice
  • Добавление отрицательного значения этого остатка к целевому времени, чтобы получить время среза:
    DATEADD(MILLISECOND, -@rms, time)

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

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

Tychon
источник
0
select from_unixtime( 600 * ( unix_timestamp( [Date] ) % 600 ) ) AS RecT, avg(Value)
from [FRIIB].[dbo].[ArchiveAnalog]
group by RecT
order by RecT;

замените два 600 на любое количество секунд, которые вы хотите сгруппировать.

Если вам это нужно часто, и таблица не меняется, как следует из названия Archive, вероятно, было бы немного быстрее преобразовать и сохранить дату (и время) как unixtime в таблице.

theking2
источник
0

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

Select 
   CONCAT( 
            Format(endtime,'yyyy-MM-dd_HH:'),  
            LEFT(Format(endtime,'mm'),1),
            '0' 
          ) as [Time-Slice]
Jesse
источник
0
select dateadd(minute, datediff(minute, 0, Date), 0),
       sum(SnapShotValue)
FROM [FRIIB].[dbo].[ArchiveAnalog]
group by dateadd(minute, datediff(minute, 0, Date), 0)
Якуб Дворжак
источник
1
Пожалуйста, предоставьте некоторые объяснения, когда вы отвечаете на вопрос.
Shizzen83