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?

cnd
fonte

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)
cnd
fonte
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.01.5enquanto datepart(hour, '1900-01-01 09:00:30.000')/2.04.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.

Michael - onde está Clay Shirky
fonte
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)

tzup
fonte
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])
Timothy Khouri
fonte
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 
Frank Schmitt
fonte
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.

Derek
fonte
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);
nobilist
fonte
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
user3193141
fonte
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)
Rodas PT
fonte
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.

tychon
fonte
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.

theking2
fonte
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]
Jessé
fonte
0
select dateadd(minute, datediff(minute, 0, Date), 0),
       sum(SnapShotValue)
FROM [FRIIB].[dbo].[ArchiveAnalog]
group by dateadd(minute, datediff(minute, 0, Date), 0)
Jakub Dvorak
fonte
1
Forneça algumas explicações quando responder a uma pergunta.
Shizzen83 22/06