sql >> Databáze >  >> RDS >> Mysql

Počítat počet po sobě jdoucích návštěv

Vynechal jsem značku mysql a napsal jsem toto řešení. Bohužel toto nefunguje v MySQL, protože nepodporuje funkce oken .

Přesto to zveřejním, protože jsem do toho vložil nějaké úsilí. Testováno pomocí PostgreSQL. Fungovalo by podobně s Oracle nebo SQL Server (nebo s jakýmkoli jiným slušným RDBMS, který podporuje funkce okna).

Testovací nastavení

CREATE TEMP TABLE v(id int, visit date);
INSERT INTO v VALUES
 (444631, '2011-11-07')
,(444631, '2011-11-06')
,(444631, '2011-11-05')
,(444631, '2011-11-04')
,(444631, '2011-11-02')
,(444631, '2011-11-01')
,(444632, '2011-12-02')
,(444632, '2011-12-03')
,(444632, '2011-12-05');

Jednoduchá verze

-- add 1 to "difference" to get number of days of the longest period
SELECT id, max(dur) + 1 as max_consecutive_days
FROM (

   -- calculate date difference of min and max in the group
   SELECT id, grp, max(visit) - min(visit) as dur
   FROM (

      -- consecutive days end up in a group
      SELECT *, sum(step) OVER (ORDER BY id, rn) AS grp
      FROM   (

         -- step up at the start of a new group of days
         SELECT id
               ,row_number() OVER w AS rn
               ,visit
               ,CASE WHEN COALESCE(visit - lag(visit) OVER w, 1) = 1
                THEN 0 ELSE 1 END AS step
         FROM   v
         WINDOW w AS (PARTITION BY id ORDER BY visit)
         ORDER  BY 1,2
         ) x
      ) y
      GROUP BY 1,2
   ) z
GROUP  BY 1
ORDER  BY 1
LIMIT  1;

Výstup:

   id   | max_consecutive_days
--------+----------------------
 444631 |                    4

Rychlejší / kratší

Později jsem našel ještě lepší způsob. grp čísla nejsou spojitá (ale neustále stoupají). Nevadí, protože to je jen prostředek k dosažení cíle:

SELECT id, max(dur) + 1 AS max_consecutive_days
FROM (
    SELECT id, grp, max(visit) - min(visit) AS dur
    FROM (
      -- subtract an integer representing the number of day from the row_number()
      -- creates a "group number" (grp) for consecutive days
      SELECT id
            ,EXTRACT(epoch from visit)::int / 86400
           - row_number() OVER (PARTITION BY id ORDER BY visit) AS grp
            ,visit
      FROM   v
      ORDER  BY 1,2
      ) x
    GROUP BY 1,2
    ) y
GROUP  BY 1
ORDER  BY 1
LIMIT  1;

SQL Fiddle pro oba.

Více



  1. SQL QUERY vícenásobné vyhledávání v jednom řádku pro nalezení dat z jiného řádku ve stejné tabulce

  2. Jak nahradit konkrétní hodnoty ve sloupci databáze Oracle?

  3. Porovnání cloudových virtuálních strojů se spravovanou cloudovou databází

  4. Načítání propojeného seznamu v databázi MySQL