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í
StartDateaEndDatehodnota) 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éstNULLna hodnotu vzdáleného data, protožeNULLhodnoty jsou řazeny před místo za ne-NULLhodnoty: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 OVERChangehodnoty (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 -1Poté
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 vWHEREnejvzdá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í:https://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.