Oracle
 sql >> база данни >  >> RDS >> Oracle

Oracle SQL - Изберете потребители между две дати по месец

Тази заявка показва броя на активните потребители в сила към края на месеца.

Как работи:

  1. Преобразувайте всеки въведен ред (с StartDate и EndDate стойност) на две редове, които представляват момент от време, когато броят на активните потребители се е увеличил (на StartDate ) и намален (на EndDate ). Трябва да конвертираме NULL към стойност на далечна дата, защото NULL стойностите са сортирани преди вместо след не-NULL стойности:

    Това прави вашите данни да изглеждат така:

    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. След това просто SUM OVER Change стойности (след сортиране), за да получите броя на активните потребители към тази конкретна дата:

    Така че първо сортирайте по 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
    

    След това 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. След това ние PARTITION (не групиране!) редовете по месеци и ги сортирайте по датата им, за да можем да идентифицираме последния ActiveCount ред за този месец (това всъщност се случва в WHERE на най-външната заявка, използвайки ROW_NUMBER() и COUNT() за всеки месец 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. След това филтрирайте това, където IsLastInMonth = 1 (всъщност, където ROW_COUNT() = COUNT(*) във всеки PARTITION ), за да ни даде крайните изходни данни:

    At-end-of-month     Active-count
    2017-01                        1
    2018-01                        3
    2019-01                        3
    2019-03                        2
    2019-06                        1
    9999-12                        0
    

Това наистина води до „празнини“ в набора от резултати, защото At-end-of-month показва само редове, където Active-count стойността всъщност се промени, вместо да включва всички възможни календарни месеци - но това е идеално (що се отнася до мен), защото изключва излишни данни. Попълването на празнините може да се направи в кода на приложението ви, като просто повтаряте изходните редове за всеки допълнителен месец, докато стигне до следващия At-end-of-month стойност.

Ето заявката, използваща T-SQL на SQL Server (в момента нямам достъп до Oracle). И ето SQLFiddle, който използвах, за да стигна до решение: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

Тази заявка може да бъдат сведени до по-малко вложени заявки чрез директно използване на агрегатни и прозоречни функции, вместо използване на псевдоними (като OtdYear , ActiveCount , и т.н.), но това би направило заявката много по-трудна за разбиране.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Не може да се изтеглят резултатите чрез подаване на вход като xml път в oracle

  2. Oracle SQL сравнява дати

  3. Заменете CHAR с VARCHAR2

  4. Как да вмъкнете низ, който съдържа &

  5. Деактивирайте и по-късно активирайте всички индекси на таблици в Oracle