Tento dotaz ukazuje počet aktivních uživatelů platný ke konci měsíce.
Jak to funguje:
-
Převeďte každý vstupní řádek (pomocí
StartDate
aEndDate
hodnota) na dva řádky, které představují časový okamžik, kdy se zvýšil počet aktivních uživatelů (vStartDate
) a snížena (naEndDate
). Potřebujeme převéstNULL
na hodnotu vzdáleného data, protožeNULL
hodnoty jsou řazeny před místo za ne-NULL
hodnoty:Díky tomu budou vaše data vypadat takto:
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
-
Pak jednoduše
SUM OVER
Change
hodnoty (po seřazení), abyste získali počet aktivních uživatelů k danému datu:Nejprve tedy seřaďte podle
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
Poté
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
-
Poté
PARTITION
(ne seskupit!) řádky podle měsíce a seřadit je podle data, abychom mohli identifikovat posledníActiveCount
řádek pro daný měsíc (ve skutečnosti se to stane vWHERE
nejvzdálenějšího dotazu pomocíROW_NUMBER()
aCOUNT()
za každý měsícPARTITION
):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
-
Potom filtrujte podle toho, kde
IsLastInMonth = 1
(ve skutečnosti, kdeROW_COUNT() = COUNT(*)
uvnitř každéhoPARTITION
), abychom dostali konečná výstupní data:At-end-of-month Active-count 2017-01 1 2018-01 3 2019-01 3 2019-03 2 2019-06 1 9999-12 0
Výsledkem jsou „mezery“ v sadě výsledků, protože At-end-of-month
sloupec zobrazuje pouze řádky, kde je Active-count
hodnota se skutečně změnila, než aby zahrnovala všechny možné kalendářní měsíce - ale to je ideální (pokud jde o mě), protože to vylučuje nadbytečná data. Vyplnění mezer lze provést v kódu aplikace pouhým opakováním výstupních řádků pro každý další měsíc, dokud nedosáhne dalšího At-end-of-month
hodnotu.
Zde je dotaz pomocí T-SQL na SQL Server (momentálně nemám přístup k Oracle). A tady je SQLFiddle, se kterým jsem přišel k řešení: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
Tento dotaz může být sloučeny do menšího počtu vnořených dotazů pomocí agregačních a okenních funkcí přímo namísto použití aliasů (jako OtdYear
, ActiveCount
, atd.), ale tím by byl dotaz mnohem obtížnější pochopit.