Como se indicó (o al menos aludió) en muchas de las maravillosas respuestas ya dadas, este problema se resuelve fácilmente una vez que tiene un conjunto de números para trabajar.
Nota: El siguiente es T-SQL, pero es simplemente mi implementación particular de conceptos generales ya mencionados aquí y en Internet en general. Debería ser relativamente simple convertir el código a su dialecto de elección.
¿Cómo? Considere esta consulta:
SELECT DATEADD(d, N, '0001-01-22')
FROM Numbers -- A table containing the numbers 0 through N
WHERE N <= 5;
Lo anterior produce el rango de fechas 1/22/0001 - 1/27/0001 y es extremadamente trivial. Hay 2 datos clave en la consulta anterior: la fecha de inicio0001-01-22
y el desplazamiento de 5
. Si combinamos estos dos datos, obviamente tenemos nuestra fecha de finalización. Por lo tanto, dadas dos fechas, la generación de un rango puede desglosarse así:
Encuentra la diferencia entre dos fechas dadas (el desplazamiento), fácil:
-- Returns 125
SELECT ABS(DATEDIFF(d, '2014-08-22', '2014-12-25'))
El uso ABS()
aquí garantiza que el orden de fechas sea irrelevante.
Genere un conjunto limitado de números, también fácil:
-- Returns the numbers 0-2
SELECT N = ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) - 1
FROM(SELECT 'A' AS S UNION ALL SELECT 'A' UNION ALL SELECT 'A')
Tenga en cuenta que en realidad no nos importa lo que estamos seleccionando FROM
aquí. Solo necesitamos un conjunto para trabajar para que podamos contar el número de filas en él. Yo personalmente uso un TVF, algunos usan un CTE, otros usan una tabla de números en su lugar, se entiende la idea. Abogo por el uso de la solución más eficiente que también comprenda.
La combinación de estos dos métodos resolverá nuestro problema:
DECLARE @date1 DATE = '9001-11-21';
DECLARE @date2 DATE = '9001-11-23';
SELECT D = DATEADD(d, N, @date1)
FROM (
SELECT N = ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) - 1
FROM (SELECT 'A' AS S UNION ALL SELECT 'A' UNION ALL SELECT 'A') S
) Numbers
WHERE N <= ABS(DATEDIFF(d, @date1, @date2));
El ejemplo anterior es un código horrible, pero demuestra cómo todo se une.
Más diversión
Necesito hacer mucho este tipo de cosas, así que encapsulé la lógica en dos TVF. El primero genera un rango de números y el segundo usa esta funcionalidad para generar un rango de fechas. La matemática es asegurar que el orden de entrada no importe y porque quería usar el rango completo de números disponibles enGenerateRangeSmallInt
.
La siguiente función tarda ~ 16 ms de tiempo de CPU para devolver el rango máximo de 65536 fechas.
CREATE FUNCTION dbo.GenerateRangeDate (
@date1 DATE,
@date2 DATE
)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN (
SELECT D = DATEADD(d, N + 32768, CASE WHEN @date1 <= @date2 THEN @date1 ELSE @date2 END)
FROM dbo.GenerateRangeSmallInt(-32768, ABS(DATEDIFF(d, @date1, @date2)) - 32768)
);
GO
CREATE FUNCTION dbo.GenerateRangeSmallInt (
@num1 SMALLINT = -32768
, @num2 SMALLINT = 32767
)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN (
WITH Numbers(N) AS (
SELECT N FROM(VALUES
(1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 16
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 32
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 48
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 64
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 80
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 96
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 112
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 128
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 144
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 160
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 176
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 192
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 208
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 224
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 240
, (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 256
) V (N)
)
SELECT TOP(ABS(CAST(@num1 AS INT) - CAST(@num2 AS INT)) + 1)
N = ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) + CASE WHEN @num1 <= @num2 THEN @num1 ELSE @num2 END - 1
FROM Numbers A
, Numbers B
);