Kueri ini menunjukkan jumlah pengguna aktif yang efektif per akhir bulan.
Cara kerjanya:
-
Konversikan setiap baris input (dengan
StartDate
danEndDate
nilai) menjadi dua baris yang mewakili titik waktu saat jumlah pengguna aktif bertambah (padaStartDate
) dan dikurangi (padaEndDate
). Kita perlu mengonversiNULL
ke nilai tanggal yang jauh karenaNULL
nilai diurutkan sebelum bukan setelah non-NULL
nilai:Ini membuat data Anda terlihat seperti ini:
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
-
Kemudian kita cukup
SUM OVER
Change
nilai (setelah diurutkan) untuk mendapatkan jumlah pengguna aktif pada tanggal tertentu:Jadi pertama, urutkan berdasarkan
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
Kemudian
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
-
Kemudian kita
PARTITION
(bukan mengelompokkan!) baris berdasarkan bulan dan mengurutkannya berdasarkan tanggalnya sehingga kami dapat mengidentifikasiActiveCount
terakhir baris untuk bulan itu (ini sebenarnya terjadi diWHERE
dari kueri terluar, menggunakanROW_NUMBER()
danCOUNT()
untuk setiap bulanPARTITION
):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
-
Kemudian filter di mana
IsLastInMonth = 1
(sebenarnya, di manaROW_COUNT() = COUNT(*)
di dalam setiapPARTITION
) untuk memberi kami data keluaran akhir:At-end-of-month Active-count 2017-01 1 2018-01 3 2019-01 3 2019-03 2 2019-06 1 9999-12 0
Hal ini menyebabkan "kesenjangan" dalam kumpulan hasil karena At-end-of-month
kolom hanya menampilkan baris di mana Active-count
nilai sebenarnya berubah daripada memasukkan semua bulan kalender yang mungkin - tetapi itu ideal (sejauh yang saya ketahui) karena tidak termasuk data yang berlebihan. Mengisi celah dapat dilakukan di dalam kode aplikasi Anda hanya dengan mengulangi baris keluaran untuk setiap bulan tambahan hingga mencapai At-end-of-month
berikutnya nilai.
Inilah kueri menggunakan T-SQL di SQL Server (Saya tidak memiliki akses ke Oracle sekarang). Dan inilah SQLFiddle yang saya gunakan untuk menemukan solusi: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
Kueri ini bisa diratakan menjadi lebih sedikit kueri bersarang dengan menggunakan fungsi agregat dan jendela secara langsung daripada menggunakan alias (seperti OtdYear
, ActiveCount
, dll) tetapi itu akan membuat kueri lebih sulit untuk dipahami.