Jak stwierdzono (lub przynajmniej wspominano) w wielu cudownych odpowiedziach już udzielonych, problem ten można łatwo rozwiązać, gdy masz zestaw liczb do pracy.
Uwaga: Poniżej znajduje się T-SQL, ale jest to po prostu moja konkretna implementacja ogólnych pojęć, o których już tu wspomniano i ogólnie w Internecie. Przekształcenie kodu na wybrany dialekt powinno być stosunkowo proste.
W jaki sposób? Rozważ to zapytanie:
SELECT DATEADD(d, N, '0001-01-22')
FROM Numbers -- A table containing the numbers 0 through N
WHERE N <= 5;
Powyższe daje zakres dat od 22.01.10001 do 27.01.10001 i jest niezwykle trywialne. Istnieją 2 kluczowe fragmenty informacji w powyższej zapytania: the data rozpoczęcia of 0001-01-22
a przesunięcie z 5
. Jeśli połączymy te dwie informacje, to oczywiście mamy datę zakończenia. Zatem biorąc pod uwagę dwie daty, generowanie zakresu można podzielić w następujący sposób:
Znajdź różnicę między dwiema podanymi datami (przesunięcie), łatwo:
-- Returns 125
SELECT ABS(DATEDIFF(d, '2014-08-22', '2014-12-25'))
Użycie ABS()
tutaj gwarantuje, że kolejność dat jest nieistotna.
Wygeneruj ograniczony zestaw liczb, również łatwy:
-- 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')
Zauważ, że tak naprawdę nie obchodzi nas, co tutaj wybieramy FROM
. Potrzebujemy tylko zestawu do pracy, aby policzyć liczbę wierszy w nim. Osobiście używam TVF, niektórzy używają CTE, inni zamiast tego używają tabeli liczb, masz pomysł. Opowiadam się za użyciem najbardziej wydajnego rozwiązania, które również rozumiesz.
Połączenie tych dwóch metod rozwiąże nasz problem:
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));
Powyższy przykład to okropny kod, ale pokazuje, jak wszystko się składa.
Więcej zabawy
Muszę dużo robić tego typu rzeczy, więc zawarłem logikę w dwóch TVF. Pierwsza generuje zakres liczb, a druga używa tej funkcji do generowania zakresu dat. Matematyka polega na tym, że kolejność wprowadzania nie ma znaczenia i ponieważ chciałem użyć pełnego zakresu liczb dostępnych w GenerateRangeSmallInt
.
Poniższa funkcja potrzebuje ~ 16 ms czasu procesora, aby zwrócić maksymalny zakres 65536 dat.
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
);