2

期間と金額を含むデータベーステーブルがあります。それらを1日あたりの期間と価格のある契約と考えてください。

start      | end        | amount_per_day
2013-01-01 | 2013-01-31 | 100
2013-02-01 | 2013-06-30 | 200
2013-01-01 | 2013-06-30 | 100
2013-05-01 | 2013-05-15 | 50
2013-05-16 | 2013-05-31 | 50

各期間の合計を表示するクエリを作成したいと思います。つまり、次のようになります。

2013-01-01から2013-01-31までは、1番目と3番目の契約がアクティブであるため、1日あたりの合計金額は200です。2013-02-01から2013-04-30までは、2番目と3番目の行がアクティブです。 、したがって、合計は300です。2013-05-01から2013-05-15までは、2行目、3行目、および4行目がアクティブであるため、合計は350です。2013-05-16から2013-05-31まで、2番目の行は3行目と5行目がアクティブであるため、合計は再び350になります。最後に、2013-06-01から2013-06-30までは、2行目と3行目のみがアクティブであるため、合計は300に戻ります。

start      | end        | total_amount_per_day
2013-01-01 | 2013-01-31 | 200
2013-02-01 | 2013-04-30 | 300
2013-05-01 | 2013-05-31 | 350
2013-06-01 | 2013-06-30 | 300

(間隔を検出し、同じ合計を持ってそれらをマージする必要はありません2013-05-01 -> 2013-05-152013-05-16 -> 2013-05-31、それは素晴らしいことです)。

ポータブルソリューションをお勧めしますが、それが不可能な場合はSQLServerも機能します。

テーブルの構造に小さな変更を加えることができるので、クエリを簡単にする場合、たとえば、期間を終了日を排他的に指定することができます(したがって、最初の期間はstart = 2013-01-01、end = 2013-02-01)そのような提案をしてください。

4

1 に答える 1

1

完全なクエリから始めて、それを分解して説明します。これはSQL-Server固有ですが、微調整を加えることで、分析機能をサポートする任意のDMBSに適合させることができます。

WITH Data AS
(   SELECT  Start, [End], Amount_Per_Day
    FROM    (VALUES
                ('20130101', '20130131', 100),
                ('20130201', '20130630', 200),
                ('20130101', '20130630', 100),
                ('20130501', '20130515', 50),
                ('20130516', '20130531', 50)
            ) t (Start, [End], Amount_Per_Day)
), Numbers AS
(   SELECT  Number
    FROM    Master..spt_values
    WHERE   Type = 'P'
), DailyData AS
(   SELECT  [Date] = DATEADD(DAY, Number, Start),
            [AmountPerDay] = SUM(Amount_Per_Day)
    FROM    Data
            INNER JOIN Numbers
                ON Number BETWEEN 0 AND DATEDIFF(DAY, Start, [End])
    GROUP BY DATEADD(DAY, Number, Start)
), GroupedData AS
(   SELECT  [Date],
            AmountPerDay,
            [GroupByValue] = DATEADD(DAY, -ROW_NUMBER() OVER(PARTITION BY AmountPerDay ORDER BY [Date]), [Date])
    FROM    DailyData
)
SELECT  [Start] = MIN([Date]),
        [End] = MAX([Date]),
        AmountPerDay
FROM    GroupedData
GROUP BY AmountPerDay, GroupByValue
ORDER BY [Start], [End];

DataCTEは単なるサンプルデータです。

CTEは、Numbers0から2047までの一連の数字です(開始日と終了日が2047日以上離れている場合、これは失敗し、わずかに調整する必要があります)

Next CTEDailyDataは、数値を使用して範囲を個々の日付に拡張するだけなので、

20130101, 20130131, 100

になる

20130101, 100
20130102, 100
20130103, 100
....
20130131, 100

次に、ROW_NUMBER関数を使用してデータを1日あたりの量でグループ化し、データがいつ変更されるかを見つけて、1日あたりの同様の量の範囲を定義し、各範囲のMINおよびMAXの日付を取得する場合です。

範囲をグループ化するこの方法の正確な動作を説明/デモンストレーションするのにいつも苦労しています。意味がわからない場合はSELECT * FROM DailyData、最後に使用して未加工の非集計データを表示するのがおそらく最も簡単です。

于 2013-01-22T08:53:01.377 に答える