標題聽起來令人困惑,但請讓我解釋一下:如何使用From/To date列從單個記錄中獲取派生'N'個日期行?
我有一個表格,它有兩列提供日期範圍,一列提供值。我需要查詢該表和「細節」的數據,如該
是否有可能只使用TSQL嗎?
附加信息
問題的表是關於2-3million記錄長而且還在不斷增長
標題聽起來令人困惑,但請讓我解釋一下:如何使用From/To date列從單個記錄中獲取派生'N'個日期行?
我有一個表格,它有兩列提供日期範圍,一列提供值。我需要查詢該表和「細節」的數據,如該
是否有可能只使用TSQL嗎?
附加信息
問題的表是關於2-3million記錄長而且還在不斷增長
假設日期的範圍是相當窄的,一個替代方案是使用一個遞歸CTE來創建一個li在範圍內的所有日期的ST然後再加入插值到它:
WITH LastDay AS
(
SELECT MAX(Date_To) AS MaxDate
FROM MyTable
),
Days AS
(
SELECT MIN(Date_From) AS TheDate
FROM MyTable
UNION ALL
SELECT DATEADD(d, 1, TheDate) AS TheDate
FROM Days CROSS JOIN LastDay
WHERE TheDate <= LastDay.MaxDate
)
SELECT mt.Item_ID, mt.Cost_Of_Item, d.TheDate
FROM MyTable mt
INNER JOIN Days d
ON d.TheDate BETWEEN mt.Date_From AND mt.Date_To;
我還假設的那個日期從和日期來表示一個包含的範圍(即包括兩個邊緣) - 在日期上使用包容性BETWEEN
是不常見的。
編輯
在遞歸CTE SQL Server中的默認MAXRECURSION
是100,這將限制在查詢日期範圍內100天的跨度。您可以將其調整爲maximum of 32767。
此外,如果你要過濾只是一個小範圍的大表的日期,你可以調整CTE限制範圍內的天數:
WITH DateRange AS
(
SELECT CAST('2014-01-01' AS DATE) AS MinDate,
CAST('2014-02-16' AS DATE) AS MaxDate
),
Days AS
(
SELECT MinDate AS TheDate
FROM DateRange
UNION ALL
SELECT DATEADD(d, 1, TheDate) AS TheDate
FROM Days CROSS APPLY DateRange
WHERE TheDate <= DateRange.MaxDate
)
SELECT mt.Item_ID, mt.Cost_Of_Item, d.TheDate
FROM MyTable mt
INNER JOIN Days d
ON d.TheDate BETWEEN mt.Date_From AND mt.Date_To
OPTION (MAXRECURSION 0);
您可以生成一個增量表,並將其加入到你的日期從:
查詢:
With inc(n) as (
Select ROW_NUMBER() over (order by (select 1)) -1 From (
Select 1 From (values(1), (1), (1), (1), (1), (1), (1), (1), (1), (1)) as x1(n)
Cross Join (values(1), (1), (1), (1), (1), (1), (1), (1), (1), (1)) as x2(n)
) as x(n)
)
Select item_id, cost, DATEADD(day, n, dateFrom), n From @dates d
Inner Join inc i on n <= DATEDIFF(day, dateFrom, dateTo)
Order by item_id
輸出:
item_id cost Date n
1 100 2014-01-01 00:00:00.000 0
1 100 2014-01-02 00:00:00.000 1
1 100 2014-01-03 00:00:00.000 2
2 105 2014-01-08 00:00:00.000 2
2 105 2014-01-07 00:00:00.000 1
2 105 2014-01-06 00:00:00.000 0
2 105 2014-01-09 00:00:00.000 3
3 102 2014-02-14 00:00:00.000 3
3 102 2014-02-15 00:00:00.000 4
3 102 2014-02-16 00:00:00.000 5
3 102 2014-02-11 00:00:00.000 0
3 102 2014-02-12 00:00:00.000 1
3 102 2014-02-13 00:00:00.000 2
示例數據:
declare @dates table(item_id int, cost int, dateFrom datetime, dateTo datetime);
insert into @dates(item_id, cost, dateFrom, dateTo) values
(1, 100, '20140101', '20140103')
, (2, 105, '20140106', '20140109')
, (3, 102, '20140211', '20140216');
這可以使用Cursors
來實現。
我模擬提供的測試數據,並創建另一個表名爲「DesiredTable」來存儲裏面的數據,並創建了以下cusror這就實現正是你在找什麼:
SET NOCOUNT ON;
DECLARE @ITEM_ID int, @COST_OF_ITEM Money,
@DATE_FROM date, @DATE_TO date;
DECLARE @DateDiff INT; -- holds number of days between from & to columns
DECLARE @counter INT = 0; -- for loop counter
PRINT '-------- Begin the Date Expanding Cursor --------';
-- defining the cursor target statement
DECLARE Date_Expanding_Cursor CURSOR FOR
SELECT [ITEM_ID]
,[COST_OF_ITEM]
,[DATE_FROM]
,[DATE_TO]
FROM [dbo].[OriginalTable]
-- openning the cursor
OPEN Date_Expanding_Cursor
-- fetching next row data into the declared variables
FETCH NEXT FROM Date_Expanding_Cursor
INTO @ITEM_ID, @COST_OF_ITEM, @DATE_FROM, @DATE_TO
-- if next row is found
WHILE @@FETCH_STATUS = 0
BEGIN
-- calculate the number of days in between the date columns
SELECT @DateDiff = DATEDIFF(day,@DATE_FROM,@DATE_TO)
-- reset the counter to 0 for the next loop
set @counter = 0;
WHILE @counter <= @DateDiff
BEGIN
-- inserting rows inside the new table
insert into DesiredTable
Values (@COST_OF_ITEM, DATEADD(day,@counter,@DATE_FROM))
set @counter = @counter +1
END
-- fetching next row
FETCH NEXT FROM Date_Expanding_Cursor
INTO @ITEM_ID, @COST_OF_ITEM, @DATE_FROM, @DATE_TO
END
-- cleanup code
CLOSE Date_Expanding_Cursor;
DEALLOCATE Date_Expanding_Cursor;
的代碼從原始表中提取每一行,然後計算DATE_FROM
和DATE_TO
列之間的天數,然後使用此數字腳本將創建要插入新表DesiredTable
內的相同行。
試試看,讓我知道結果。
它作爲一種魅力,謝謝你。只有一個問題:所討論的表大約需要2-3百萬條記錄(並且正在增長),所以使用遊標真的非常耗費資源,而且需要很長時間才能完成。 – dhuesca
哦,對不起。我在創建此解決方案時不知道這些信息。原諒我 :) –
另一種方法是創建和維護日曆表,其中包含所有日期多年(在我們的應用程序中,我們有30年左右的表,每年延長)。然後,你可以鏈接到日曆:
select <whatever you need>, calendar.day
from <your tables> inner join calendar on calendar.day between <min date> and <max date>
這種方法允許包括日曆表的附加信息(節假日等) - 有時是非常有幫助的。
謝謝你的回答,嘗試了小提琴,工作得很好,在我的服務器上試過......沒有那麼多...忘了說這個表是2mill記錄長,當執行代碼時我得到了一個530錯誤。努力更好地瞭解您的查詢,看看我能否修復它。 – dhuesca
看起來數據中的日期範圍大於100天 - 我已經更新了一些更多的想法。 – StuartLC
感謝您的更新,它可以根據需要運行。 – dhuesca