2017-02-27 86 views
1

比方說,我有如下表:SQL服務器 - 分割記錄在多個月爲記錄以一個月爲

ID | user | from  | to   | loan 
1 | BB | 01/01/2016 | 01/05/2016 | 50 
2 | AD | 01/01/2016 | 01/03/2016 | 25 
3 | AD | 01/03/2016 | 17/05/2016 | 30 

此表中的loan爲每月一次。因此,用戶BB在2016年1月1日至2016年5月1日期間每月賺取50個硬幣。我想將其分成以下記錄:

ID | user | from  | to   | loan 
1 | BB | 01/01/2016 | 01/02/2016 | 50 
1 | BB | 01/02/2016 | 01/03/2016 | 50 
1 | BB | 01/03/2016 | 01/04/2016 | 50 
1 | BB | 01/04/2016 | 01/05/2016 | 50 
2 | AD | 01/01/2016 | 01/02/2016 | 25 
2 | AD | 01/02/2016 | 01/03/2016 | 25 
3 | AD | 01/03/2016 | 01/04/2016 | 30 
3 | AD | 01/04/2016 | 01/05/2016 | 30 
3 | AD | 01/05/2016 | 17/05/2016 | 30 

有關如何完成此任務的任何想法/建議?

+0

無論是遞歸CTE或日曆表,您與您的數據 –

+1

加入爲什麼沒有('01/03/2016 - 01/04/2016','01/04/2016 - 01/05/2016'和'01/05/2016 - 17/05/2016')? – GarethD

+0

@GarethD我不好,我錯過了那邊的一張唱片。編輯。 – DenStudent

回答

3

你可以試試這個:

SET DATEFORMAT dmy; 
DECLARE @Dummy TABLE(ID INT,[user] VARCHAR(100),[from] DATE,[to] DATE,loan INT); 
INSERT INTO @Dummy VALUES 
(1,'BB','01/01/2016','01/05/2016',50) 
,(2,'AD','01/01/2016','01/03/2016',25) 
,(3,'AD','01/03/2016','17/05/2016',30); 

DECLARE @FirstDay DATE='01/01/2016'; 

WITH SomeNumbers AS --replace this with a values/tally/numbers/date table 
(
    SELECT * FROM(VALUES(0),(1),(2),(3)) AS t(Nr) 
) 


SELECT DISTINCT 
     x.FirstOfMonth 
     ,FittingData.* 
FROM SomeNumbers 
CROSS APPLY(SELECT DATEADD(MONTH,SomeNumbers.Nr,@FirstDay) AS FirstOfMonth) AS x 
CROSS APPLY(SELECT * FROM @Dummy AS d WHERE x.FirstOfMonth>=d.[from] AND x.FirstOfMonth<d.[to]) AS FittingData 
WHERE ID IS NOT NULL 
ORDER BY ID,[from] 

結果

+--------------+----+------+------------+------------+------+ 
| FirstOfMonth | ID | user | from  | to   | loan | 
+--------------+----+------+------------+------------+------+ 
| 2016-01-01 | 1 | BB | 2016-01-01 | 2016-05-01 | 50 | 
+--------------+----+------+------------+------------+------+ 
| 2016-02-01 | 1 | BB | 2016-01-01 | 2016-05-01 | 50 | 
+--------------+----+------+------------+------------+------+ 
| 2016-03-01 | 1 | BB | 2016-01-01 | 2016-05-01 | 50 | 
+--------------+----+------+------------+------------+------+ 
| 2016-04-01 | 1 | BB | 2016-01-01 | 2016-05-01 | 50 | 
+--------------+----+------+------------+------------+------+ 
| 2016-01-01 | 2 | AD | 2016-01-01 | 2016-03-01 | 25 | 
+--------------+----+------+------------+------------+------+ 
| 2016-02-01 | 2 | AD | 2016-01-01 | 2016-03-01 | 25 | 
+--------------+----+------+------------+------------+------+ 
| 2016-03-01 | 3 | AD | 2016-03-01 | 2016-05-17 | 30 | 
+--------------+----+------+------------+------------+------+ 
| 2016-04-01 | 3 | AD | 2016-03-01 | 2016-05-17 | 30 | 
+--------------+----+------+------------+------------+------+ 
1
;WITH testtable(ID,[USER],[from],[to],loan)AS(
    SELECT 1,'BB',CONVERT(DATE,'01/01/2016'),CONVERT(DATE,'01/05/2016'),50 UNION all 
    SELECT 2,'AD','01/01/2016','01/03/2016',25 UNION all 
    SELECT 3 ,'AD','01/03/2016','01/05/2016',30 
) 
SELECT t.ID,t.[USER],DATEADD(d,sv.number,t.[FROM]) AS [From],dateadd(d,sv.number+1,t.[FROM]) AS [To],t.loan FROM testtable AS t 
INNER JOIN master.dbo.spt_values AS sv ON sv.type='P' AND sv.number BETWEEN 0 AND DATEDIFF(d,t.[FROM],t.[TO])-1 
 
ID   USER From  To   loan 
----------- ---- ---------- ---------- ----------- 
1   BB 2016-01-01 2016-01-02 50 
1   BB 2016-01-02 2016-01-03 50 
1   BB 2016-01-03 2016-01-04 50 
1   BB 2016-01-04 2016-01-05 50 
2   AD 2016-01-01 2016-01-02 25 
2   AD 2016-01-02 2016-01-03 25 
3   AD 2016-01-03 2016-01-04 30 
3   AD 2016-01-04 2016-01-05 30