sql >> Databasteknik >  >> RDS >> Oracle

Oracle SQL - Välj användare mellan två datum för månad

Den här frågan visar antalet aktiva användare som gäller i slutet av månaden.

Hur det fungerar:

  1. Konvertera varje inmatningsrad (med StartDate och EndDate värde) till två rader som representerar en tidpunkt när antalet aktiva användare ökade (på StartDate ) och minskas (på EndDate ). Vi måste konvertera NULL till ett långt borta datumvärde eftersom NULL värden sorteras före istället för efter icke-NULL värden:

    Detta får din data att se ut så här:

    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. Sedan SUM OVER helt enkelt Change värden (efter sortering) för att få antalet aktiva användare från det specifika datumet:

    Så först, sortera efter 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
    

    Sedan 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. Sedan PARTITION (inte gruppera!) raderna efter månad och sortera dem efter deras datum så att vi kan identifiera den senaste ActiveCount rad för den månaden (detta händer faktiskt i WHERE för den yttersta frågan med ROW_NUMBER() och COUNT() för varje månad 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. Filtrera sedan på det där IsLastInMonth = 1 (faktiskt där ROW_COUNT() = COUNT(*) inuti varje PARTITION ) för att ge oss de slutliga utdata:

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

Detta resulterar i "luckor" i resultatuppsättningen eftersom At-end-of-month kolumnen visar bara rader där Active-count värdet ändrades faktiskt snarare än att inkludera alla möjliga kalendermånader - men det är idealiskt (såvitt jag är orolig) eftersom det utesluter överflödig data. Fylla i luckorna kan göras i din applikationskod genom att helt enkelt upprepa utmatningsraderna för varje ytterligare månad tills den når nästa At-end-of-month värde.

Här är frågan med T-SQL på SQL Server (jag har inte tillgång till Oracle just nu). Och här är SQLFiddle som jag använde för att komma fram till en lösning: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

Den här frågan kan plattas till färre kapslade frågor genom att använda aggregat- och fönsterfunktioner direkt istället för att använda alias (som OtdYear , ActiveCount , etc) men det skulle göra frågan mycket svårare att förstå.



  1. använda PHP för att skapa en HTML-tabell från en MSQL-fråga, utan dubbletter av rader?

  2. Det går inte att använda None (NULL)-värden i python mysql.connector i förberedd INSERT-sats

  3. Hitta senaste objektändringar i SQL Server Database

  4. mysql:Hämta senaste konversationsposter av användare