2016-04-12 18 views
0

ソース日:T/SQL - グループ/乗算レコード

CREATE TABLE #Temp (ID INT Identity(1,1) Primary Key, BeginDate datetime, EndDate datetime, GroupBy INT) 
INSERT INTO #Temp 
SELECT '2015-06-05 00:00:00.000','2015-06-12 00:00:00.000',7 
UNION 
SELECT '2015-06-05 00:00:00.000', '2015-06-08 00:00:00.000',7 
UNION 
SELECT '2015-10-22 00:00:00.000', '2015-10-31 00:00:00.000',7 

SELECT *, DATEDIFF(DAY,BeginDate, EndDate) TotalDays FROM #Temp 
DROP TABLE #Temp 

ID BeginDate  EndDate   GroupBy  TotalDays 
1 6/5/15 0:00  6/8/15 0:00  7   3 
2 6/5/15 0:00  6/12/15 0:00 7   7 
3 10/22/15 0:00 10/31/15 0:00 7   9 

所望の出力:

ID BeginDate  EndDate   GroupBy TotalDays GroupCnt GroupNum 
1 6/5/15 0:00  6/8/15 0:00  7  3   1   1 
2 6/5/15 0:00  6/12/15 0:00 7  7   1   1 
3 10/22/15 0:00 10/29/15 0:00 7  9   2   1 
3 10/29/15 0:00 10/31/15 0:00 7  9   2   2 

目標:

グループID/BeginDate/EndDateに基づいてレコード。 GROUPBYに基づいて数(# of days)TotalDays(days diff)、 はGROUPBY => TotalDays場合、他のTotalDays制限内に滞在しながら、グループレコードを(GROUPBY回数ごとに1つのレコードの)乗算単一グループレコード を保ちます。

紛らわしいのですが、基本的に上記の例では、各グループに1つのレコードがあります。(ID/BeginDate/EndDate)は、日差がb/w Begin/End date = 7 or less(GroupBy)のレコードです。

差分が7日を超える場合は、別のレコードを作成します(差分を7日間追加するたびに)。

第1の2つのレコードの日数が7日以内であるため、レコードは1つだけです。

3番目のレコードの日数はです。したがって、2つのレコードが必要です(最初の7日間は第1回、追加の2日間は第2回)。

GroupCNT = how many records there're of the grouped records after applying the above records. 

グループ番号は、基本的にグループのrow numberです。

GroupBy#はレコードごとに異なる場合があります。データセットは膨大なので、パフォーマンスは重要です。

私が知ることができた1つのパターンは、モジュラスb/w GroupByと日差に関連していました。

GroupBy value is < days diffの場合、係数は常にGroupByより小さい。 GroupBy value = days diffの場合、係数は常に0になります。GroupBy value > days diffの場合、係数は常にGroupByに等しくなります。私は/それをグループ化するために/どのように要件を満たすためにレコードを掛けるかわからない。

SELECT DISTINCT 
    ID 
, BeginDate 
, EndDate 
, GroupBy 
, DATEDIFF(DAY,BeginDate, EndDate) TotalDays 
, CAST(GroupBy as decimal(18,6))%CAST(DATEDIFF(DAY,BeginDate, EndDate) AS decimal(18,6)) Modulus 
, CASE WHEN DATEDIFF(DAY,BeginDate, EndDate) <= GroupBy THEN BeginDate END NewBeginDate 
, CASE WHEN DATEDIFF(DAY,BeginDate, EndDate) <= GroupBy THEN EndDate END NewEndDate 
FROM #Temp 

更新: は言及し忘れた/ /終了日を開始し、レコードが掛け取得すると、それに応じて変更されることがあります。言い換えれば、開始日/終了日にはGroupByが反映されます.3番目と4番目のレコードに、より明確に意味するものが目的の出力に表示されます。 また、GroupCnt/GroupNumは、レコードのグループ化/乗算と同じくらい重要ではありません。あなたは再帰CTEを使用して、このような何かを行うことができ

答えて

3

..

;WITH cte AS (
    SELECT ID, 
      BeginDate, 
      EndDate, 
      GroupBy, 
      DATEDIFF(DAY, BeginDate, EndDate) AS TotalDays, 
      1 AS GroupNum 
    FROM #Temp 
    UNION ALL 
    SELECT ID, 
      BeginDate, 
      EndDate, 
      GroupBy, 
      TotalDays, 
      GroupNum + 1 
    FROM cte 
    WHERE GroupNum * GroupBy < TotalDays 
) 
SELECT ID, 
     BeginDate = CASE WHEN GroupNum = 1 THEN BeginDate 
         ELSE DATEADD(DAY, GroupBy * (GroupNum - 1), BeginDate) 
         END , 
     EndDate = CASE WHEN TotalDays <= GroupBy THEN EndDate 
         WHEN DATEADD(DAY, GroupBy * GroupNum, BeginDate) > EndDate THEN EndDate 
         ELSE DATEADD(DAY, GroupBy * GroupNum, BeginDate) 
         END , 
     GroupBy, 
     TotalDays, 
     COUNT(*) OVER (PARTITION BY ID) GroupCnt, 
     GroupNum 
FROM cte 
OPTION (MAXRECURSION 0) 

CTEは、このようなレコードセットを構築します。

ID   BeginDate    EndDate     GroupBy  TotalDays GroupNum 
----------- ----------------------- ----------------------- ----------- ----------- ----------- 
1   2015-06-05 00:00:00.000 2015-06-08 00:00:00.000 7   3   1 
2   2015-06-05 00:00:00.000 2015-06-12 00:00:00.000 7   7   1 
3   2015-10-22 00:00:00.000 2015-10-31 00:00:00.000 7   9   1 
3   2015-10-22 00:00:00.000 2015-10-31 00:00:00.000 7   9   2 

、あなたはこれを取り、開始日と終了日がどうあるべきかを決定するために、いくつかのケースステートメントを使用する必要があります。あなたは、SQL 2012を使用しているので、

あなたは

ID   BeginDate    EndDate     GroupBy  TotalDays GroupCnt GroupNum 
----------- ----------------------- ----------------------- ----------- ----------- ----------- ----------- 
1   2015-06-05 00:00:00.000 2015-06-08 00:00:00.000 7   3   1   1 
2   2015-06-05 00:00:00.000 2015-06-12 00:00:00.000 7   7   1   1 
3   2015-10-22 00:00:00.000 2015-10-29 00:00:00.000 7   9   2   1 
3   2015-10-29 00:00:00.000 2015-10-31 00:00:00.000 7   9   2   2 

で終わる必要があり、また、あなたの最後のクエリでLAGとLEAD関数を使用することができます。

;WITH cte AS (
    SELECT ID, 
      BeginDate, 
      EndDate, 
      GroupBy, 
      DATEDIFF(DAY, BeginDate, EndDate) AS TotalDays, 
      1 AS GroupNum 
    FROM #Temp 
    UNION ALL 
    SELECT ID, 
      BeginDate, 
      EndDate, 
      GroupBy, 
      TotalDays, 
      GroupNum + 1 
    FROM cte 
    WHERE GroupNum * GroupBy < TotalDays 
) 

SELECT ID, 
     BeginDate = COALESCE(LAG(BeginDate) OVER (PARTITION BY ID ORDER BY GroupNum) + GroupBy * (GroupNum - 1), BeginDate), 
     EndDate = COALESCE(LEAD(BeginDate) OVER (PARTITION BY ID ORDER BY GroupNum) + GroupBy * GroupNum, EndDate), 
     GroupBy, 
     TotalDays, 
     COUNT(*) OVER (PARTITION BY ID) GroupCnt, 
     GroupNum 
FROM cte 
OPTION (MAXRECURSION 0) 
+0

最初のソリューションは、かなりきちんとしています。ありがとう! - 今私はそれを理解する必要があります...ハハ。 2番目の解決策として、私はEndDate = BeginDateを取得しています。同じことをしていますか? – 007

+1

LAG関数とLEAD関数は現在動作しているはずです:) – JamieD77

1
CREATE TABLE dim_number (id INT); 
INSERT INTO dim_number VALUES ((0), (1), (2), (3)); -- Populate this to a large number 

SELECT 
    #Temp.Id, 
    CASE WHEN dim_number.id = 0 
     THEN #Temp.BeginDate 
     ELSE DATEADD(DAY, dim_number.id  * #Temp.GroupBy, #Temp.BeginDate) 
    END                AS BeginDate, 
    CASE WHEN dim_number.id = parts.count 
     THEN #Temp.EndDate 
     ELSE DATEADD(DAY, (dim_number.id + 1) * #Temp.GroupBy, #Temp.BeginDate) 
    END                AS EndDate, 
    #Temp.GroupBy             AS GroupBy, 
    DATEDIFF(DAY, #Temp.BeginDate, #Temp.EndDate)     AS TotalDays, 
    parts.count + 1             AS GroupCnt, 
    dim_number.id + 1            AS GroupNum 
FROM 
    #Temp 
CROSS APPLY 
    (SELECT DATEDIFF(DAY, #Temp.BeginDate, #Temp.EndDate)/#Temp.GroupBy AS count) AS parts 
INNER JOIN 
    dim_number 
     ON dim_number.id >= 0 
     AND dim_number.id <= parts.count 
+0

この解決策のための@Mattballieに感謝します...私は言う必要がありますが、ここで何が起こっているのかを理解するためにしばらく時間がかかります。 :) – 007