sql >> データベース >  >> RDS >> Sqlserver

SQL での日付範囲交差分割

    この問題で発生する問題は、データ セットが大きくなるにつれて、TSQL で解決するソリューションが適切に拡張されなくなることです。以下では、オンザフライで構築された一連の一時テーブルを使用して問題を解決しています。数値テーブルを使用して、各日付範囲エントリをそれぞれの日に分割します。これは、主に無限に見えるオープン範囲の NULL 値が原因でスケーリングされない場所であるため、変換の範囲を実現可能な時間に制限する、はるかに将来の固定日付に切り替える必要があります。各日の最適化されたレンダリングのために、適切なインデックスを使用して日付のテーブルまたはカレンダー テーブルを作成することで、パフォーマンスが向上する可能性があります。

    範囲が分割されると、XML PATH を使用して説明がマージされるため、範囲シリーズの各日は、すべての説明が一覧表示されます。 PersonID と Date による行の番号付けにより、2 つの NOT EXISTS チェックを使用して各範囲の最初と最後の行を見つけ、一致する PersonID と Description セットの前の行が存在しない場合、または次の行が存在しない場合を見つけることができます。一致する PersonID と Description セットが存在しません。

    この結果セットは、ROW_NUMBER を使用して再番号付けされ、最終結果を構築するためにペアにすることができます。

    /*
    SET DATEFORMAT dmy
    USE tempdb;
    GO
    CREATE TABLE Schedule
    ( PersonID int, 
     Surname nvarchar(30), 
     FirstName nvarchar(30), 
     Description nvarchar(100), 
     StartDate datetime, 
     EndDate datetime)
    GO
    INSERT INTO Schedule VALUES (18, 'Smith', 'John', 'Poker Club', '01/01/2009', NULL)
    INSERT INTO Schedule VALUES (18, 'Smith', 'John', 'Library', '05/01/2009', '18/01/2009')
    INSERT INTO Schedule VALUES (18, 'Smith', 'John', 'Gym', '10/01/2009', '28/01/2009')
    INSERT INTO Schedule VALUES (26, 'Adams', 'Jane', 'Pilates', '03/01/2009', '16/02/2009')
    GO
    
    */
    
    SELECT 
     PersonID, 
     Description, 
     theDate
    INTO #SplitRanges
    FROM Schedule, (SELECT DATEADD(dd, number, '01/01/2008') AS theDate
        FROM master..spt_values
        WHERE type = N'P') AS DayTab
    WHERE theDate >= StartDate 
      AND theDate <= isnull(EndDate, '31/12/2012')
    
    SELECT 
     ROW_NUMBER() OVER (ORDER BY PersonID, theDate) AS rowid,
     PersonID, 
     theDate, 
     STUFF((
      SELECT '/' + Description
      FROM #SplitRanges AS s
      WHERE s.PersonID = sr.PersonID 
        AND s.theDate = sr.theDate
      FOR XML PATH('')
      ), 1, 1,'') AS Descriptions
    INTO #MergedDescriptions
    FROM #SplitRanges AS sr
    GROUP BY PersonID, theDate
    
    
    SELECT 
     ROW_NUMBER() OVER (ORDER BY PersonID, theDate) AS ID, 
     *
    INTO #InterimResults
    FROM
    (
     SELECT * 
     FROM #MergedDescriptions AS t1
     WHERE NOT EXISTS 
      (SELECT 1 
       FROM #MergedDescriptions AS t2 
       WHERE t1.PersonID = t2.PersonID 
         AND t1.RowID - 1 = t2.RowID 
         AND t1.Descriptions = t2.Descriptions)
    UNION ALL
     SELECT * 
     FROM #MergedDescriptions AS t1
     WHERE NOT EXISTS 
      (SELECT 1 
       FROM #MergedDescriptions AS t2 
       WHERE t1.PersonID = t2.PersonID 
         AND t1.RowID = t2.RowID - 1
         AND t1.Descriptions = t2.Descriptions)
    ) AS t
    
    SELECT DISTINCT 
     PersonID, 
     Surname, 
     FirstName
    INTO #DistinctPerson
    FROM Schedule
    
    SELECT 
     t1.PersonID, 
     dp.Surname, 
     dp.FirstName, 
     t1.Descriptions, 
     t1.theDate AS StartDate, 
     CASE 
      WHEN t2.theDate = '31/12/2012' THEN NULL 
      ELSE t2.theDate 
     END AS EndDate
    FROM #DistinctPerson AS dp
    JOIN #InterimResults AS t1 
     ON t1.PersonID = dp.PersonID
    JOIN #InterimResults AS t2 
     ON t2.PersonID = t1.PersonID 
      AND t1.ID + 1 = t2.ID 
      AND t1.Descriptions = t2.Descriptions
    
    DROP TABLE #SplitRanges
    DROP TABLE #MergedDescriptions
    DROP TABLE #DistinctPerson
    DROP TABLE #InterimResults
    
    /*
    
    DROP TABLE Schedule
    
    */
    

    上記のソリューションは、追加の説明間のギャップも処理するため、ギャップを残して PersonID 18 の別の説明を追加する場合:

    INSERT INTO Schedule VALUES (18, 'Smith', 'John', 'Gym', '10/02/2009', '28/02/2009')
    

    適度に隙間を埋めてくれます。コメントで指摘されているように、このテーブルに名前情報を含めるべきではありません。最終結果で結合できる Persons テーブルに正規化する必要があります。 SELECT DISTINCT を使用して一時テーブルを作成し、その JOIN を作成することで、この別のテーブルをシミュレートしました。



    1. json応答を作成する方法

    2. plpgsqlを介してPostgresからテーブルの主キーを取得するにはどうすればよいですか?

    3. Postgresqlの初期構成:postgresユーザーとしてアクセスする方法は?

    4. AndroidでMySQLへのプールされた接続を確立する