Як згрупувати час за годиною або за 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. чи можна зробити виведення дати без мілісекунд?

Відповіді:


213

нарешті зроблено с

GROUP BY
DATEPART(YEAR, DT.[Date]),
DATEPART(MONTH, DT.[Date]),
DATEPART(DAY, DT.[Date]),
DATEPART(HOUR, DT.[Date]),
(DATEPART(MINUTE, DT.[Date]) / 10)

11
Я зробив це ROUND((DATEPART(MINUTE, DT.[Date]) / 5),0,1) * 5так, що коли я дивлюся на дані, він співвідноситься з найближчим часовим інтервалом
hdost

7
Рік, місяць і день можна спростити до DATE(DT.[Date]).

@Keelan не працює для мене - проте CONVERT (дата, DT. [Дата]) робить.
Dan Parsonson

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 дня.
Якщо ваші дані насправді охоплюють століття чи тисячоліття і досі точні до другої чи мілісекунди… вітання! Що б ви не робили, продовжуйте робити це.


чи можна було б це зробити з конкретної дати початку?
Піландер

1
@pylander Звичайно. Просто поставте whereпункт перед group by.
Майкл - Де Клей Ширкий

2
Я бачу. То чи змінити це було / 20 * 20б так само, як збирати дані протягом 20-хвилинних інтервалів? Вибачте, я зараз борюся з математикою.
однобокою

2
для SECOND як 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 - найменша дата, яку ви можете використовувати при такому підході.
jeroenh

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(як запропонував Тимофій)


1
ГРУПА ДО [Дата], ДАТЕПАРТ (год, год [дата]) - це безлад, чи не так?
cнд

1
@nCdy Має бути все гаразд, інакше ви отримаєте помилку "... недійсна у списку вибору, оскільки вона не міститься ні в сукупній функції, ні в пункті GROUP BY"
tzup

1
Як ви групуєтесь за цілою датою та за годинами? Я просто використовую Min Date.
CND

якщо ви використовуєте Min (Date), то, звичайно, ви можете зняти Date в групі.
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 не має сенсу.
Тар

Домовились з Тар. Угрупування не має сенсу.
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 для вищезазначеного?
Біранчі


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

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)

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

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


0
select from_unixtime( 600 * ( unix_timestamp( [Date] ) % 600 ) ) AS RecT, avg(Value)
from [FRIIB].[dbo].[ArchiveAnalog]
group by RecT
order by RecT;

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

Якщо вам це потрібно часто, і таблиця не змінюється, як підказує ім’я Архів, можливо, це буде трохи швидше конвертувати та зберігати дату (& час) як одноразову в таблиці.


0

Я знаю, що я запізнююсь на шоу з цим, але я використав цей - досить простий підхід. Це дозволяє отримати 60-хвилинні фрагменти без жодних проблем із округленням.

Select 
   CONCAT( 
            Format(endtime,'yyyy-MM-dd_HH:'),  
            LEFT(Format(endtime,'mm'),1),
            '0' 
          ) as [Time-Slice]

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.