Truy vấn này hiển thị số lượng người dùng đang hoạt động có hiệu lực vào cuối tháng.
Cách hoạt động:
-
Chuyển đổi từng hàng đầu vào (với
StartDate
vàEndDate
giá trị) thành hai các hàng đại diện cho một điểm trong thời gian khi số lượng người dùng hoạt động tăng lên (trênStartDate
) và giảm dần (vàoEndDate
). Chúng ta cần chuyển đổiNULL
thành một giá trị ngày xa vìNULL
các giá trị được sắp xếp trước thay vì sau non-NULL
giá trị:Điều này làm cho dữ liệu của bạn trông giống như sau:
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
-
Sau đó, chúng ta chỉ cần
SUM OVER
Change
giá trị (sau khi sắp xếp) để có được số người dùng đang hoạt động kể từ ngày cụ thể đó:Vì vậy, trước tiên, hãy sắp xếp theo
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
Sau đó,
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
-
Sau đó, chúng tôi
PARTITION
(không phải nhóm!) các hàng theo tháng và sắp xếp chúng theo ngày của chúng để chúng tôi có thể xác địnhActiveCount
cuối cùng hàng cho tháng đó (điều này thực sự xảy ra trongWHERE
của truy vấn ngoài cùng, sử dụngROW_NUMBER()
vàCOUNT()
cho mỗi thángPARTITION
):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
-
Sau đó lọc trên đó
IsLastInMonth = 1
(trên thực tế, trong đóROW_COUNT() = COUNT(*)
bên trong mỗiPARTITION
) để cung cấp cho chúng tôi dữ liệu đầu ra cuối cùng:At-end-of-month Active-count 2017-01 1 2018-01 3 2019-01 3 2019-03 2 2019-06 1 9999-12 0
Điều này dẫn đến "khoảng trống" trong tập kết quả vì At-end-of-month
cột chỉ hiển thị các hàng có Active-count
giá trị thực sự đã thay đổi thay vì bao gồm tất cả các tháng theo lịch có thể có - nhưng đó là lý tưởng (theo tôi nghĩ) vì nó loại trừ dữ liệu dư thừa. Việc lấp đầy các khoảng trống có thể được thực hiện bên trong mã ứng dụng của bạn bằng cách chỉ cần lặp lại các hàng đầu ra cho mỗi tháng bổ sung cho đến khi nó đến At-end-of-month
tiếp theo giá trị.
Đây là truy vấn sử dụng T-SQL trên SQL Server (tôi không có quyền truy cập vào Oracle ngay bây giờ). Và đây là SQLFiddle mà tôi đã sử dụng để tìm ra giải pháp: 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
Truy vấn này có thể được làm phẳng thành ít truy vấn lồng nhau hơn bằng cách sử dụng trực tiếp các hàm tổng hợp và cửa sổ thay vì sử dụng bí danh (như OtdYear
, ActiveCount
, v.v.) nhưng điều đó sẽ làm cho truy vấn khó hiểu hơn nhiều.