2017-07-14 8 views
0

私は非常に基本的なSQLユーザーです。私は基本といくつかの中間スキルを知っていますが、私はこの質問を書くことに問題があります。結果セットを使用してエントリ間の日付を生成する

私はヒストリーと呼ばれるテーブルを持っており、それは特定のデータのユニークなエントリを含んでいます。ここでは、この表には、どのように見えるかのストリップダウンバージョンです:私がする必要がどのような

+-------------------------+----+-----------+-------+ 
| Time     | ID | ChangeNum | Value | 
+-------------------------+----+-----------+-------+ 
| 2014-07-03 00:00:00.000 | 3 | 0   | A 
+-------------------------+----+-----------+-------+ 
| 2014-10-02 00:00:00.000 | 3 | 1   | B 
+-------------------------+----+-----------+-------+ 
| 2014-11-27 00:00:00.000 | 3 | 2   | C 
+-------------------------+----+-----------+-------+ 
| 2015-01-15 00:00:00.000 | 3 | 3   | D 
+-------------------------+----+-----------+-------+ 
| 2015-02-14 00:00:00.000 | 3 | 4   | E 
+-------------------------+----+-----------+-------+ 
| 2015-09-02 00:00:00.000 | 3 | 5   | F 
+-------------------------+----+-----------+-------+ 
| 2015-09-04 00:00:00.000 | 3 | 6   | G 
+-------------------------+----+-----------+-------+ 
| 2016-09-13 00:00:00.000 | 3 | 7   | H 
+-------------------------+----+-----------+-------+ 
| 2016-09-14 00:00:00.000 | 3 | 8   | I 
+-------------------------+----+-----------+-------+ 
| 2017-02-12 00:00:00.000 | 3 | 9   | J 
+-------------------------+----+-----------+-------+ 
| 2017-02-18 00:00:00.000 | 3 | 10  | K 
+-------------------------+----+-----------+-------+ 

すると、同じ値の残りの部分を維持しながら、これらの日付範囲の間でデータを生成し、ビューを作成しています。例えば、ここにテーブルが

+-------------------------+----+-----------+ 
    | Time     | ID | ChangeNum | 
    +-------------------------+----+-----------+ 
    | 2014-07-03 00:00:00.000 | 3 | 0   | 
    +-------------------------+----+-----------+ 
    | 2014-07-04 00:00:00.000 | 3 | 0   | 
    +-------------------------+----+-----------+ 
    | 2014-07-05 00:00:00.000 | 3 | 0   | 
    +-------------------------+----+-----------+ 
    | 2014-07-04 00:00:00.000 | 3 | 0   | 
    +-------------------------+----+-----------+ 
    |  truncated for readability ...  | 
    +-------------------------+----+-----------+ 
    | 2014-10-01 00:00:00.000 | 3 | 0   | 
    +-------------------------+----+-----------+ 
    | 2014-10-02 00:00:00.000 | 3 | 1   | 
    +-------------------------+----+-----------+ 

どのように見えるかのサブセットです私はCTEを使用して、日付範囲を生成することができ、このような記事を見てきました、それは十分に簡単です。ただし、履歴表の結果セットをループしてを実行し、日付範囲の下限と上限を取得し(最初の行の時間フィールド、次に次の行の時間フィールド)、これらの間にデータを生成します行。これは私がそれを見せるより簡単かもしれませんが、私は少し失われています。私の最初の考えは、カーソルを使用することでしたが、私はテーブルの遅れ/先導という文脈でこれをどうやって行うのか正確には分かりません。どんな助け?ありがとう。私はあなたの例のデータに基づいて、いくつかの仮定を作ってるんだけれどもここ

+1

あなたは間違いなくこのためのカーソルは必要ありません。タリーテーブルが必要です。 –

+0

2つの答えは、私が聞いたことのない集計表を使用しています。正規のSQL構文を使用してこれを達成できますか?(例:変数やテーブル作成を使用しない)また、これは「ギャップとアイランド」の解決策の良い候補ではないのですか、間違っていますか? – psrpsrpsr

+0

@ psrpsrpsr私の答えは集計表を使用していませんが、変数や集計表なしでこれを行うことはできません。欠落している日付レコードを作成するには、どちらか一方が必要になります。 –

答えて

1

は、これを行うための楽しい方法です:

--This is your current table 
CREATE TABLE #TEST 
(timefield datetime, 
id int, 
ChangeNum int) 

INSERT INTO #TEST (TIMEFIELD, ID, CHANGENUM) 
VALUES 
('2014-07-03 00:00:00.000', 3, 0), 
('2014-10-02 00:00:00.000', 3, 1), 
('2014-11-27 00:00:00.000', 3, 2), 
('2015-01-15 00:00:00.000', 3, 3), 
('2015-02-14 00:00:00.000', 3, 4) 

--This is your destination table 
CREATE TABLE #TEST2 
(timefield datetime, 
id int, 
ChangeNum int) 

--This is where we INSERT from your source to destination table 
DECLARE @TIMEFIELD datetime = '2014-07-03 00:00:00.000' --Your start date 
DECLARE @ChangeNum int = 0 --Starting ChangeNum 

WHILE @TIMEFIELD <= '2015-02-14 00:00:00.000' --Your end date 
BEGIN 
INSERT INTO #TEST2 
SELECT @TIMEFIELD, 3, @ChangeNum; 

SET @TIMEFIELD = DATEADD(DD, 1, @TIMEFIELD); 

IF EXISTS (SELECT * FROM #TEST WHERE TIMEFIELD = @TIMEFIELD) 
    BEGIN 
    SET @ChangeNum = (SELECT ChangeNum FROM #TEST WHERE TIMEFIELD = @TIMEFIELD) 
    --This part can be modified to account for more columns 
    END 
END 

SELECT * FROM #TEST2 --The new table 

それも異なる日付で、すべてのIDのために動作しますので、編集は、物事を変更します変更番号:

CREATE TABLE #TEST 
(timefield datetime, 
id int, 
ChangeNum int) 

INSERT INTO #TEST (TIMEFIELD, ID, CHANGENUM) 
VALUES 
('2014-07-03 00:00:00.000', 3, 0), 
('2014-10-02 00:00:00.000', 3, 1), 
('2014-11-27 00:00:00.000', 3, 2), 
('2015-01-15 00:00:00.000', 3, 3), 
('2015-02-14 00:00:00.000', 3, 4), 
('2014-11-27 00:00:00.000', 2, 2), 
('2015-01-15 00:00:00.000', 2, 3), 
('2015-02-14 00:00:00.000', 2, 4), 
('2014-10-02 00:00:00.000', 1, 1), 
('2014-11-27 00:00:00.000', 1, 2), 
('2015-01-15 00:00:00.000', 1, 3), 
('2015-02-14 00:00:00.000', 1, 4) 

CREATE TABLE #TEST2 
(timefield datetime, 
id int, 
ChangeNum int) 

DECLARE @ID int = (SELECT MIN(ID) FROM #TEST) 
DECLARE @ChangeNum int = (SELECT MIN(ChangeNum) FROM #TEST WHERE @ID = ID) 
DECLARE @TIMEFIELD datetime = (SELECT MIN(TIMEFIELD) FROM #TEST WHERE @ID = ID) 

WHILE @ID <= (SELECT MAX(ID) FROM #TEST) 
BEGIN 

WHILE @TIMEFIELD <= (SELECT MAX(TIMEFIELD) FROM #TEST WHERE ID = @ID) 
BEGIN 
    INSERT INTO #TEST2 
    SELECT @TIMEFIELD, @ID, @ChangeNum 
    SET @TIMEFIELD = DATEADD(DD, 1, @TIMEFIELD) 

    IF EXISTS (SELECT * FROM #TEST WHERE TIMEFIELD = @TIMEFIELD AND ID = @ID) 
    BEGIN 
    SET @ChangeNum = (SELECT ChangeNum FROM #TEST WHERE TIMEFIELD = @TIMEFIELD AND ID = @ID); 
    END 

    END 

    IF EXISTS (SELECT MIN(ID) FROM #TEST WHERE ID > @ID) 
    BEGIN 
    SET @ID = (SELECT MIN(ID) FROM #TEST WHERE ID > @ID) 
    SET @ChangeNum = (SELECT MIN(ChangeNum) FROM #TEST WHERE @ID = ID) 
    SET @TIMEFIELD = (SELECT MIN(TIMEFIELD) FROM #TEST WHERE @ID = ID) 
    END 
END 

SELECT * FROM #TEST2 
+0

素晴らしい - これはかなりまっすぐだった。私は自分の環境の詳細(必要なテーブルや変数、特定のSQL Engineの構文など)に変更しましたが、これは魅力的でした。ありがとう! – dxh9845

+0

@ dxh9845それを聞いてうれしい、これは楽しい1つでした!あなたがこれを今必要としているかどうかはわかりませんが、ID = 3のためにハードコードされていて大したことはできませんでしたので、各ID/ChangeNum /日付の組み合わせ。 –

+0

正直、かなり面白い - 私はループで動作するようにこれを有効にしていたが、あなたは私にそれを打つ。ありがとう! – dxh9845

0

これは楽しいものでした。 Seanが言ったように、あなたは集計表が必要です。 This is where I got the Dates table from.これは、2つの変更が同じ日に行われた場合に起こることを考慮に入れていません。私はまた、テストを容易にするために時間枠を短くしました。これは、複数のIDが返されることを考慮に入れます。

CREATE TABLE #test (ID INT, ChangeNum INT, [Value] varchar(1), [Time] datetime); 

    DECLARE @StartDate datetime 
     , @CutoffDate datetime; 

INSERT INTO #test ([time], ID, ChangeNum, [Value]) 
VALUES 
('2011-07-03 00:00:00.000', 3, 0, 'A'), 
('2011-07-10 00:00:00.000', 3, 1, 'B'), 
('2011-07-15 00:00:00.000', 3, 2, 'C'),  
('2011-07-01 00:00:00.000', 2, 0, 'Q'), 
('2011-07-06 00:00:00.000', 2, 1, 'R'), 
('2011-08-03 00:00:00.000', 2, 2, 'S');  

    SELECT @StartDate = MIN([Time]) 
     ,@CutoffDate = MAX([time]) 
    FROM #test; 

WITH Dates 
AS (SELECT d 
    FROM (
     SELECT d = DATEADD(DAY, rn - 1, @StartDate) 
     FROM (SELECT TOP (DATEDIFF(DAY, @StartDate, @CutoffDate)) rn = ROW_NUMBER() OVER (
        ORDER BY s1.[object_id]) 
      FROM sys.all_objects AS s1 
      CROSS JOIN sys.all_objects AS s2 
      ORDER BY s1.[object_id] 
      ) AS x 
     ) AS y 
    ) 
    ,ChangeRanges 
    AS (
     SELECT ID 
      , [Time] BEGIN_DATE 
      , CASE WHEN LEAD ([Time], 1,0) OVER (PARTITION BY ID ORDER BY ID, [time]) = '1900-01-01 00:00:00.000' THEN [Time] 
        ELSE DATEADD(DAY, -1, LEAD ([Time], 1,0) OVER (PARTITION BY ID ORDER BY ID, [time])) END END_DATE 
      , ChangeNum 
     FROM #test 
)  
    SELECT d.d [DATE] 
      ,cr.ID 
      ,cr.ChangeNum 
    FROM ChangeRanges cr 
    JOIN Dates d ON d.d >= cr.BEGIN_DATE AND d.d <= cr.END_DATE 
    WHERE ID = 3 
    GROUP BY cr.ID, d.d, cr.ChangeNum 
    ORDER BY cr.ID, d.d 

DROP TABLE #test 
+0

私の答えを明確にするだけでは、集計表は使用されませんが、必ずしも必要ではありません。 –

+0

私は@AaronDietzに同意します。集計表は必ずしも必要ではありません。反復するソリューションが負担がかかりすぎるか、作業できないなど、あまりにも多くのインスタンスが存在します(たとえばレポートなど)。 SSMSでリストが必要な場合は、ループが正常に機能します。 – SteveB

+0

私はまたあなたが2008年であったことに気付かなかった。私のバージョンはそのバージョンでは動作しません。 – SteveB

関連する問題