Como agrupar o tempo por hora ou 10 minutos


167

como quando eu faço

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

como posso especificar o período do grupo?

MS SQL 2008

2ª Edição

estou tentando

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

mudou% 10 para / 10. é possível gerar saída de data sem milissegundos?

Respostas:


213

finalmente terminou com

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

11
Eu fiz isso ROUND((DATEPART(MINUTE, DT.[Date]) / 5),0,1) * 5, de modo que quando eu olhar para os dados que está correlacionada com a ranhura mais próxima vez
— hdost

7
Ano, mês e dia podem ser simplificados para DATE(DT.[Date]).

@ Keelan Não funciona para mim - no entanto, CONVERT (data, DT. [Data]) funciona.
— Dan Parsonson

datepart(hour, workingPolicy.workingHours)/2.0dá 1.5enquanto datepart(hour, '1900-01-01 09:00:30.000')/2.0dá 4.5, não entendo o porquê? Nota: workingPolicy.workingHours = 1900-01-01 09: 00: 30.000 . Por favor, ajude
— affanBajwa

65

Estou muito atrasado para a festa, mas isso não aparece em nenhuma das respostas existentes:

GROUP BY DATEADD(MINUTE, DATEDIFF(MINUTE, '2000', date_column) / 10 * 10, '2000')
  • Os termos 10e MINUTEpodem ser alterados para qualquer número e DATEPART, respectivamente.
  • É um DATETIMEvalor, o que significa:
    • Funciona bem em longos intervalos de tempo. (Não há colisão entre os anos.)
    • A inclusão na SELECTdeclaração fornecerá à sua saída uma coluna com uma saída bonita truncada no nível que você especificar.
  • '2000'é uma "data âncora" em torno da qual o SQL executará a matemática da data. Jereonh descobriu abaixo que você encontra um estouro de número inteiro com a âncora anterior ( 0) ao agrupar datas recentes por segundos ou milissegundos. †
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]

Se seus dados se estenderem por séculos, ‡ usando uma única data de ancoragem para o agrupamento de segundo ou milissegundo ainda encontrará o estouro. Se isso estiver acontecendo, você pode solicitar a cada linha que ancore a comparação de classificação na meia-noite da sua própria data:

  • Use em DATEADD(DAY, DATEDIFF(DAY, 0, aa.[date]), 0)vez de '2000'onde quer que apareça acima. Sua consulta será totalmente ilegível, mas funcionará.

  • Uma alternativa pode ser CONVERT(DATETIME, CONVERT(DATE, aa.[date]))a substituição.

† 2 32 ≈ 4,29E + 9, portanto, se DATEPARTfor SECOND, você recebe 4,3 bilhões de segundos de cada lado, ou "ancora ± 136 anos". Da mesma forma, 2 32 milissegundos é de ≈ 49,7 dias.
‡ Se os seus dados ultrapassam séculos ou milênios e ainda são precisos até o segundo ou milissegundo ... parabéns! O que quer que você esteja fazendo, continue fazendo.


seria possível fazer isso a partir de uma data de início específica?
— Pylander

1
@pylander Claro. Basta colocar uma wherecláusula antes do group by.
— Michael - Onde está Clay Shirky

2
Entendo. Então, alterar isso / 20 * 20seria o mesmo que coletar dados para intervalos de 20 minutos? Desculpe, estou com dificuldades em matemática agora.
— Dispensado

2
para SEGUNDO como DATEPART, recebo uma mensagem de erro ( 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.). Parece que MINUTE é o menor período que você pode usar com essa abordagem.
— jeroenh

1
@ jeroenh, você está correto. Eu adicionei uma seção para falar sobre isso. tldr: altere 0para '2000'(aspas são importantes!) e tente SECONDnovamente.
— Michael - Onde está Clay Shirky

17

No T-SQL, você pode:

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

ou

por minuto de uso DATEPART(mi, [Date])

ou

por 10 minutos de uso DATEPART(mi, [Date]) / 10(como sugeriu Timothy)


1
GRUPO POR [Data], DATEPART (hh, [Data]) está uma bagunça, não é?
— CND

1
@nCdy Deve estar tudo bem, caso contrário, você receberá um erro "... inválido na lista de seleção porque não está contido em uma função agregada ou na cláusula GROUP BY"
— tzup 15/02/11

1
Como você agrupa por data inteira e por horas ao mesmo tempo? Eu só estou usando Data mínima.
— CND

se você usar Mín (Data), é claro que poderá retirar a Data no Agrupar por.
— TZUP

3
Minutos divididos por 10 criarão agrupamentos por intervalos de 10 minutos, o que é necessário. O módulo 10 não faz sentido.
— Tar

12

Por um intervalo de 10 minutos, você

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

Como já foi mencionado por tzup e Pieter888 ... para fazer um intervalo de horas, apenas

GROUP BY DATEPART(HOUR, [Date])

2
mas aqui agrupo minutos do ano de 1980 com minutos de 2011: o SI precisa se preocupar com isso.
— cnd

% Seria a matemática correta? Isso não criaria 10 grupos. Por exemplo: 1% 10 = 9 2% 10 = 8 Também não seria necessariamente o agrupamento cronológico correto. Eu acho que apenas uma divisão normal seria correta. A menos que% não seja uma divisão restante no sql.
— Steven

12
Minutos divididos por 10 criarão agrupamentos por intervalos de 10 minutos, o que é necessário. O módulo 10 não faz sentido.
— Tar

Concordou com Tar. O agrupamento não faz sentido.
— MikeMurko # 7/16

7

Deve ser algo como

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

(Não tenho 100% de certeza sobre a sintaxe - sou mais um cara do tipo Oracle)

No 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

A resposta original que o autor deu funciona muito bem. Só para estender essa ideia, você pode fazer algo como

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

o que permitirá que você agrupe por um período mais longo do que 60 minutos, por exemplo, 720, que é meio dia etc.


1
Qual seria a consulta do MySQL para o acima?
— Biranchi 12/09

4

Para o MySql:

GROUP BY
DATE(`your_date_field`),
HOUR(`your_date_field`),
FLOOR( MINUTE(`your_date_field`) / 10);

1

Minha solução é usar uma função para criar uma tabela com os intervalos de datas e associá-la aos dados que eu quero agrupar usando o intervalo de datas na tabela. O intervalo de datas pode ser facilmente selecionado ao apresentar os dados.

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
Bem-vindo ao stackoverflow. Você precisa explicar completamente sua resposta e o que isso acrescenta às outras 10 respostas já existentes no site.
— Simon.SA

0

Para o SQL Server 2012, embora eu acredite que funcionaria no SQL Server 2008R2, uso a seguinte abordagem para reduzir o tempo de milissegundos:

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

Isso funciona por:

  • Obtendo o número de milissegundos entre um ponto fixo e o tempo de destino:
    @ms = DATEDIFF(MILLISECOND, CAST(time AS DATE), time)
  • Tomando o restante da divisão desses milissegundos em fatias de tempo:
    @rms = @ms % @msPerSlice
  • Adicionando o negativo desse restante ao tempo de destino para obter o tempo de fatia:
    DATEADD(MILLISECOND, -@rms, time)

Infelizmente, como isso transborda em microssegundos e unidades menores, conjuntos de dados maiores e mais precisos precisariam usar um ponto fixo menos conveniente.

Não comparei rigorosamente isso e não estou no big data, portanto sua milhagem pode variar, mas o desempenho não foi notavelmente pior do que os outros métodos testados em nossos equipamentos e conjuntos de dados, e o pagamento na conveniência do desenvolvedor por fatias arbitrárias vale a pena para nós.


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

substitua os dois 600 por qualquer número de segundos que você deseja agrupar.

Se você precisar disso com frequência e a tabela não mudar, como o nome Archive sugere, provavelmente seria um pouco mais rápido converter e armazenar a data (e hora) como um tempo unix na tabela.


0

Eu sei que estou atrasado para o show com este, mas eu usei isso - uma abordagem bem simples. Isso permite que você obtenha as fatias de 60 minutos sem problemas de arredondamento.

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
Forneça algumas explicações quando responder a uma pergunta.
— Shizzen83 22/06
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.