ตามที่ระบุไว้ (หรืออย่างน้อยพูดพาดพิงถึง) ในคำตอบที่ยอดเยี่ยมจำนวนมากที่ได้รับแล้วปัญหานี้จะแก้ไขได้อย่างง่ายดายเมื่อคุณมีชุดของตัวเลขที่จะทำงานกับ
หมายเหตุ:ต่อไปนี้คือ T-SQL แต่เป็นเพียงการนำแนวคิดทั่วไปของฉันไปใช้ที่นี่และบนอินเทอร์เน็ตเป็นจำนวนมาก มันควรจะง่ายในการแปลงรหัสเป็นภาษาที่คุณเลือก
อย่างไร? พิจารณาคำถามนี้:
SELECT DATEADD(d, N, '0001-01-22')
FROM Numbers -- A table containing the numbers 0 through N
WHERE N <= 5;
ด้านบนสร้างช่วงวันที่ 1/22/0001 - 1/27/0001 และไม่สำคัญมาก มี 2 ชิ้นสำคัญของข้อมูลในแบบสอบถามดังกล่าวข้างต้นคือ: วันที่เริ่มต้นของ0001-01-22
และชดเชย5
ของ หากเรารวมข้อมูลสองชิ้นนี้เข้าด้วยกันเราก็จะมีวันที่สิ้นสุด ดังนั้นเมื่อได้รับสองวันการสร้างช่วงสามารถแบ่งได้ดังนี้:
ค้นหาความแตกต่างระหว่างสองวันที่กำหนด (ชดเชย) ง่าย:
-- Returns 125
SELECT ABS(DATEDIFF(d, '2014-08-22', '2014-12-25'))
ใช้ABS()
ที่นี่เพื่อให้แน่ใจว่าคำสั่งวันที่ไม่เกี่ยวข้อง
สร้างชุดตัวเลขที่ จำกัด และทำได้ง่าย:
-- 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')
สังเกตว่าเราไม่สนใจสิ่งที่เราเลือกFROM
ที่นี่ เราแค่ต้องตั้งค่าให้ทำงานด้วยเพื่อที่เราจะนับจำนวนแถวในนั้น ส่วนตัวผมใช้ TVF บางคนใช้ CTE ส่วนคนอื่นใช้ตารางตัวเลขแทนคุณจะได้ความคิด ฉันแนะนำให้ใช้วิธีที่มีประสิทธิภาพที่สุดที่คุณเข้าใจ
การรวมสองวิธีเหล่านี้จะช่วยแก้ปัญหาของเรา:
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));
ตัวอย่างข้างต้นเป็นรหัสที่น่ากลัว แต่แสดงให้เห็นว่าทุกอย่างมารวมกันได้อย่างไร
สนุกมาก
ฉันต้องทำสิ่งนี้มากดังนั้นฉันจึงสรุปเหตุผลเป็นสอง TVF ตัวแรกสร้างช่วงของตัวเลขและตัวที่สองใช้ฟังก์ชันนี้เพื่อสร้างช่วงของวันที่ GenerateRangeSmallInt
คณิตศาสตร์คือเพื่อให้แน่ใจเพื่อป้อนข้อมูลที่ไม่สำคัญและเพราะผมต้องการที่จะใช้อย่างเต็มรูปแบบของตัวเลขในใช้ได้
ฟังก์ชั่นต่อไปนี้ใช้เวลาประมาณ 16ms ของเวลา CPU เพื่อส่งคืนช่วงสูงสุดวันที่ 65536
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
);