sql >> Databáze >  >> RDS >> Oracle

Oracle SQL – Vyberte uživatele ze dvou dat podle měsíce

Tento dotaz ukazuje počet aktivních uživatelů platný ke konci měsíce.

Jak to funguje:

  1. Převeďte každý vstupní řádek (pomocí StartDate a EndDate hodnota) na dva řádky, které představují časový okamžik, kdy se zvýšil počet aktivních uživatelů (v StartDate ) a snížena (na EndDate ). Potřebujeme převést NULL na hodnotu vzdáleného data, protože NULL hodnoty jsou řazeny před místo za ne-NULL hodnoty:

    Díky tomu budou vaše data vypadat takto:

    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. Pak jednoduše SUM OVER Change hodnoty (po seřazení), abyste získali počet aktivních uživatelů k danému datu:

    Nejprve tedy seřaďte podle 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
    

    Poté 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. Poté PARTITION (ne seskupit!) řádky podle měsíce a seřadit je podle data, abychom mohli identifikovat poslední ActiveCount řádek pro daný měsíc (ve skutečnosti se to stane v WHERE nejvzdálenějšího dotazu pomocí ROW_NUMBER() a COUNT() za každý měsíc 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. Potom filtrujte podle toho, kde IsLastInMonth = 1 (ve skutečnosti, kde ROW_COUNT() = COUNT(*) uvnitř každého PARTITION ), abychom dostali konečná výstupní data:

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

Výsledkem jsou „mezery“ v sadě výsledků, protože At-end-of-month sloupec zobrazuje pouze řádky, kde je Active-count hodnota se skutečně změnila, než aby zahrnovala všechny možné kalendářní měsíce - ale to je ideální (pokud jde o mě), protože to vylučuje nadbytečná data. Vyplnění mezer lze provést v kódu aplikace pouhým opakováním výstupních řádků pro každý další měsíc, dokud nedosáhne dalšího At-end-of-month hodnotu.

Zde je dotaz pomocí T-SQL na SQL Server (momentálně nemám přístup k Oracle). A tady je SQLFiddle, se kterým jsem přišel k řešení: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

Tento dotaz může být sloučeny do menšího počtu vnořených dotazů pomocí agregačních a okenních funkcí přímo namísto použití aliasů (jako OtdYear , ActiveCount , atd.), ale tím by byl dotaz mnohem obtížnější pochopit.



  1. Spočítat počet různých řádků pro více hodnot

  2. Porozumění časování operátora prováděcího plánu

  3. Odstranit duplikáty z velké datové sady (> 100 milionů řádků)

  4. SQL dotaz Vybrat první pořadí 1 řádek z více úrovní/skupiny