Mysql
 sql >> Datenbank >  >> RDS >> Mysql

Zählen Sie die Anzahl der aufeinanderfolgenden Besuche

Ich habe das mysql-Tag verpasst und diese Lösung geschrieben. Leider funktioniert dies nicht in MySQL, da es keine Fensterfunktionen unterstützt .

Ich poste es trotzdem, da ich mir Mühe gegeben habe. Getestet mit PostgreSQL. Würde ähnlich mit Oracle oder SQL Server (oder jedem anderen anständigen RDBMS, das Fensterfunktionen unterstützt) funktionieren.

Testaufbau

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');

Einfache Version

-- 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;

Ausgabe:

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

Schneller / Kürzer

Später fand ich einen noch besseren Weg. grp Zahlen sind nicht kontinuierlich (sondern kontinuierlich steigend). Macht nichts, denn das sind nur Mittel zum Zweck:

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 für beide.

Mehr