Oracle
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Oracle

Oracle SQL - Chọn người dùng giữa hai ngày theo tháng

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:

  1. Chuyển đổi từng hàng đầu vào (với StartDateEndDate 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ên StartDate ) và giảm dần (vào EndDate ). Chúng ta cần chuyển đổi NULL 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
    
  2. 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
    
  3. 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 định ActiveCount cuối cùng hàng cho tháng đó (điều này thực sự xảy ra trong WHERE của truy vấn ngoài cùng, sử dụng ROW_NUMBER()COUNT() cho mỗi tháng PARTITION ):

    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
    
  4. Sau đó lọc trên đó IsLastInMonth = 1 (trên thực tế, trong đó ROW_COUNT() = COUNT(*) bên trong mỗi PARTITION ) để 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.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Java JDBC không thể kết nối với cơ sở dữ liệu Oracle bằng SSL

  2. Hàm JSON_VALUE () trong Oracle

  3. Oracle SQL tạo các cấp dữ liệu khác nhau từ một bảng duy nhất

  4. Làm thế nào để sao chép lược đồ với các đặc quyền tương tự của tài khoản hiện có trong oracle?

  5. Cách lấy dữ liệu theo tuần trong Oracle