Eu preciso encontrar, para um determinado segundo atual e limite superior e inferior, encontrar todos os segundos que cairiam dentro disso, envolvendo a marca de 59 segundos.
Com essa palavra salada fora do caminho, aqui estão alguns exemplos de entradas e saídas.
- Se a entrada for 58 segundos e o intervalo for +/- 5 segundos, então eu quero retornar 58, 59, 0, 1, 2, 3 AND 57, 56, 55, 54, 53
- Se a entrada for 22 e o mesmo intervalo, quero retornar 22, 23, 24, 25, 26, 27 E 21, 20, 19, 18, 17
- Se o Input for 5 e o mesmo intervalo, então quero retornar 0, 1, 2, 3, 4, 5 AND 5, 7, 8, 9, 10.
Eu tenho brincado com o tipo de dados TIME para tentar usar o datemath embutido para lidar com a quebra da marca de 59 segundos, mas tive sucesso limitado. Incluído aqui está minha versão completa que não funciona.
Minhas desculpas pelo estado do código, estou trabalhando nele há um tempo e estou optando por deixar todos os meus bits e bobs para ilustrar completamente o que tentei. Se eu não conseguir encontrar uma solução mais elegante, terei que construir uma tabela de contagem com a resposta "correta" para nosso valor de intervalo inicial, mas gostaria de ter mais flexibilidade do que isso, se possível.
DECLARE @CurrentSecond SMALLINT = 22 -- DATEPART(SECOND, SYSUTCDATETIME())
DECLARE @LowerBoundary INT = -5
DECLARE @UpperBoundary INT = 5
;WITH CTE_Parts AS
(
SELECT P.ID
, LowerBound = DATEPART(SECOND, DATEADD(SECOND, @LowerBoundary, CONVERT(TIME, '00:00:' + CONVERT(NVARCHAR(20), P.ID))))
--, lowerBoundTime = DATEADD(SECOND, @LowerBoundary, CONVERT(TIME, '00:00:' + CONVERT(NVARCHAR(20), P.ID)))
, UpperBound = DATEPART(SECOND, DATEADD(SECOND, @UpperBoundary , CONVERT(TIME, '00:00:' + CONVERT(NVARCHAR(20), P.ID))))
--, UpperBoundTime = DATEADD(SECOND, @UpperBoundary , CONVERT(TIME, '00:00:' + CONVERT(NVARCHAR(20), P.ID)))
, C.LowerBoundary_Current
--, C.LowerBoundTime_Current
, C.UpperBoundary_Current
--, C.UpperBoundTime_Current
FROM (VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9)
, (10), (11), (12), (13), (14), (15), (16), (17), (18), (19)
, (20), (21), (22), (23), (24), (25), (26), (27), (28), (29)
, (30), (31), (32), (33), (34), (35), (36), (37), (38), (39)
, (40), (41), (42), (43), (44), (45), (46), (47), (48), (49)
, (50), (51), (52), (53), (54), (55), (56), (57), (58), (59)
) AS P (ID)
OUTER APPLY (SELECT DATEPART(SECOND, DATEADD(SECOND, @LowerBoundary, CONVERT(TIME, '00:00:' + CONVERT(NVARCHAR(20), @CurrentSecond)))) AS LowerBoundary_Current
, LowerBoundTime_Current = DATEADD(SECOND, @LowerBoundary, CONVERT(TIME, '00:00:' + CONVERT(NVARCHAR(20), @CurrentSecond)))
, DATEPART(SECOND, DATEADD(SECOND, @UpperBoundary, CONVERT(TIME, '00:00:' + CONVERT(NVARCHAR(20), @CurrentSecond)))) AS UpperBoundary_Current
, UpperBoundTime_Current = DATEADD(SECOND, @UpperBoundary, CONVERT(TIME, '00:00:' + CONVERT(NVARCHAR(20), @CurrentSecond)))
) AS C
)
SELECT *
, Ignore = CASE WHEN @CurrentSecond + @UpperBoundary <= 59 AND @CurrentSecond + @LowerBoundary >= 0 AND ID BETWEEN LowerBoundary_Current AND UpperBoundary_Current THEN 1
WHEN NOT(@CurrentSecond + @UpperBoundary <= 59 AND @CurrentSecond + @LowerBoundary >= 0)
AND ID >= LowerBoundary_Current
OR ID <= UpperBoundary_Current
THEN 1
ELSE 0 END
FROM CTE_Parts AS P
ORDER BY P.ID
Para ser absolutamente claro, o acima NÃO faz o que eu quero (exceto me dar uma lista de valores de ID de 0-06)
Supondo que você deseja que o intervalo de segundos seja retornado em um único conjunto de resultados (não um grupo acima, mais um grupo abaixo), o seguinte pode fazer o que você precisa.
Para os exemplos fornecidos, ele retorna os resultados esperados(*).
(*) Acho que seu terceiro exemplo tem um erro de digitação, se seguir o mesmo padrão dos outros dois exemplos, acredito que deveria ser " Se o Input for 5 e o mesmo intervalo, então quero retornar 0, 1, 2 , 3, 4, 5 E 6 , 7, 8, 9, 10. "
Uma pequena variação na resposta de Rob - você realmente não precisa usar funções de data.
Eu provavelmente escreveria isso: