Як згрупувати час за годиною або за 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.