Như đã nêu (hoặc ít nhất là ám chỉ) trong nhiều câu trả lời tuyệt vời đã được đưa ra, vấn đề này dễ dàng được giải quyết một khi bạn có một bộ số để làm việc.
Lưu ý: Dưới đây là T-SQL nhưng đơn giản là việc triển khai các khái niệm chung của tôi đã được đề cập ở đây và trên internet nói chung. Nó sẽ tương đối đơn giản để chuyển đổi mã theo phương ngữ bạn chọn.
Làm sao? Xem xét truy vấn này:
SELECT DATEADD(d, N, '0001-01-22')
FROM Numbers -- A table containing the numbers 0 through N
WHERE N <= 5;
Ở trên tạo ra phạm vi ngày 1/22/0001 - 1/27/0001 và cực kỳ tầm thường. Có 2 phần quan trọng của thông tin trong các truy vấn trên: các ngày bắt đầu của 0001-01-22
và bù đắp của 5
. Nếu chúng ta kết hợp hai thông tin này thì rõ ràng chúng ta có ngày kết thúc. Do đó, trong hai ngày, việc tạo một phạm vi có thể được chia nhỏ như vậy:
Tìm sự khác biệt giữa hai ngày đã cho (phần bù), dễ dàng:
-- Returns 125
SELECT ABS(DATEDIFF(d, '2014-08-22', '2014-12-25'))
Sử dụng ABS()
ở đây đảm bảo rằng thứ tự ngày là không liên quan.
Tạo một bộ số giới hạn, cũng dễ dàng:
-- 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')
Lưu ý rằng chúng tôi không thực sự quan tâm những gì chúng tôi đang chọn FROM
ở đây. Chúng ta chỉ cần một tập hợp để làm việc để chúng ta đếm số lượng hàng trong đó. Cá nhân tôi sử dụng TVF, một số sử dụng CTE, một số khác sử dụng bảng số thay thế, bạn hiểu ý. Tôi ủng hộ việc sử dụng giải pháp hiệu quả nhất mà bạn cũng hiểu.
Kết hợp hai phương pháp này sẽ giải quyết vấn đề của chúng tôi:
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));
Ví dụ trên là mã khủng khiếp nhưng chứng minh làm thế nào mọi thứ kết hợp với nhau.
Vui hơn
Tôi cần phải làm điều này rất nhiều vì vậy tôi đã gói gọn logic vào hai TVF. Cái đầu tiên tạo ra một dãy số và cái thứ hai sử dụng chức năng này để tạo ra một phạm vi ngày. Toán học là để đảm bảo rằng thứ tự đầu vào không thành vấn đề và vì tôi muốn sử dụng đầy đủ các số có sẵn trong GenerateRangeSmallInt
.
Chức năng sau đây mất ~ 16ms thời gian CPU để trả về phạm vi tối đa 65536 ngày.
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
);