Deze zoekopdracht toont het aantal actieve gebruikers dat van kracht is aan het einde van de maand.
Hoe het werkt:
-
Converteer elke invoerrij (met
StartDate
enEndDate
waarde) in twee rijen die een tijdstip vertegenwoordigen waarop het aantal actieve gebruikers werd verhoogd (opStartDate
) en verlaagd (opEndDate
). We moetenNULL
. converteren naar een verre datumwaarde omdatNULL
waarden worden eerder gesorteerd in plaats van na niet-NULL
waarden:Hierdoor zien uw gegevens er als volgt uit:
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
-
Dan
SUM OVER
deChange
waarden (na sortering) om het aantal actieve gebruikers op die specifieke datum te krijgen:Sorteer dus eerst op
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
Dan
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
-
Dan
PARTITION
(niet groeperen!) de rijen per maand en sorteer ze op hun datum zodat we de laatsteActiveCount
kunnen identificeren rij voor die maand (dit gebeurt eigenlijk in deWHERE
van de buitenste zoekopdracht, met behulp vanROW_NUMBER()
enCOUNT()
voor elke maandPARTITION
):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
-
Filter vervolgens op dat waar
IsLastInMonth = 1
(eigenlijk, waarROW_COUNT() = COUNT(*)
binnen elkePARTITION
) om ons de uiteindelijke uitvoergegevens te geven:At-end-of-month Active-count 2017-01 1 2018-01 3 2019-01 3 2019-03 2 2019-06 1 9999-12 0
Dit resulteert wel in "gaten" in de resultatenset omdat de At-end-of-month
kolom toont alleen rijen waar de Active-count
waarde eigenlijk veranderd in plaats van alle mogelijke kalendermaanden op te nemen - maar dat is ideaal (wat mij betreft) omdat het overbodige gegevens uitsluit. Het opvullen van de hiaten kan in uw applicatiecode worden gedaan door simpelweg de uitvoerrijen voor elke extra maand te herhalen totdat het de volgende bereikt At-end-of-month
waarde.
Hier is de query met behulp van T-SQL op SQL Server (ik heb nu geen toegang tot Oracle). En hier is de SQLFiddle die ik gebruikte om tot een oplossing te komen: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
Deze zoekopdracht kan worden afgevlakt tot minder geneste zoekopdrachten door aggregatie- en vensterfuncties rechtstreeks te gebruiken in plaats van aliassen te gebruiken (zoals OtdYear
, ActiveCount
, enz.), maar dat zou de vraag veel moeilijker te begrijpen maken.