このクエリは、月末現在の有効なアクティブユーザー数を示しています。
仕組み:
-
各入力行を変換します(
StartDate
を使用) およびEndDate
値)を2つに アクティブユーザー数が増加した時点を表す行(StartDate
) )およびデクリメント(EndDate
)。NULL
を変換する必要がありますNULL
であるため、遠い日付の値になります 値は、NULL
以外の後ではなく前にソートされます 値:これにより、データは次のようになります。
OnThisDate Change 2018-01-01 1 2019-01-01 -1 2018-01-01 1 9999-12-31 -1 2019-01-01 1 2019-06-01 -1 2017-01-01 1 2019-03-01 -1
-
次に、単に
SUM OVER
Change
その特定の日付の時点でのアクティブユーザー数を取得するための値(並べ替え後):したがって、最初に
OnThisDate
で並べ替えます :OnThisDate Change 2017-01-01 1 2018-01-01 1 2018-01-01 1 2019-01-01 1 2019-01-01 -1 2019-03-01 -1 2019-06-01 -1 9999-12-31 -1
次に、
SUM OVER
:OnThisDate ActiveCount 2017-01-01 1 2018-01-01 2 2018-01-01 3 2019-01-01 4 2019-01-01 3 2019-03-01 2 2019-06-01 1 9999-12-31 0
-
次に、
PARTITION
(グループ化ではありません!)行を月ごとに並べ替え、日付で並べ替えて、最後のActiveCount
を識別できるようにします。 その月の行(これは実際にはWHERE
で発生しますROW_NUMBER()
を使用した最も外側のクエリの およびCOUNT()
毎月PARTITION
):OnThisDate ActiveCount IsLastInMonth 2017-01-01 1 1 2018-01-01 2 0 2018-01-01 3 1 2019-01-01 4 0 2019-01-01 3 1 2019-03-01 2 1 2019-06-01 1 1 9999-12-31 0 1
-
次に、
IsLastInMonth = 1
でフィルタリングします。 (実際には、ROW_COUNT() = COUNT(*)
各PARTITION
内 )最終的な出力データを提供するには:At-end-of-month Active-count 2017-01 1 2018-01 3 2019-01 3 2019-03 2 2019-06 1 9999-12 0
At-end-of-month
が原因で、結果セットに「ギャップ」が生じます。 列には、Active-count
がある行のみが表示されます 値は、考えられるすべての暦月を含めるのではなく、実際に変更されましたが、冗長なデータが除外されるため、(私に関する限り)理想的です。ギャップを埋めるには、次のAt-end-of-month
に到達するまで、追加の月ごとに出力行を繰り返すだけで、アプリケーションコード内で行うことができます。 値。
これは、SQL ServerでT-SQLを使用したクエリです(現在、Oracleにアクセスできません)。そして、これが私が解決策にたどり着いたSQLFiddleです: http://sqlfiddle.com/# !18 / ad68b7 / 24
SELECT
OtdYear,
OtdMonth,
ActiveCount
FROM
(
-- This query adds columns to indicate which row is the last-row-in-month ( where RowInMonth == RowsInMonth )
SELECT
OnThisDate,
OtdYear,
OtdMonth,
ROW_NUMBER() OVER ( PARTITION BY OtdYear, OtdMonth ORDER BY OnThisDate ) AS RowInMonth,
COUNT(*) OVER ( PARTITION BY OtdYear, OtdMonth ) AS RowsInMonth,
ActiveCount
FROM
(
SELECT
OnThisDate,
YEAR( OnThisDate ) AS OtdYear,
MONTH( OnThisDate ) AS OtdMonth,
SUM( [Change] ) OVER ( ORDER BY OnThisDate ASC ) AS ActiveCount
FROM
(
SELECT
StartDate AS [OnThisDate],
1 AS [Change]
FROM
tbl
UNION ALL
SELECT
ISNULL( EndDate, DATEFROMPARTS( 9999, 12, 31 ) ) AS [OnThisDate],
-1 AS [Change]
FROM
tbl
) AS sq1
) AS sq2
) AS sq3
WHERE
RowInMonth = RowsInMonth
ORDER BY
OtdYear,
OtdMonth
このクエリはできます エイリアス(OtdYear
など)を使用する代わりに、集計関数とウィンドウ関数を直接使用することで、ネストされたクエリの数を減らします。 、ActiveCount
など)が、クエリを理解するのがはるかに難しくなります。