sql >> Database >  >> RDS >> Mysql

Tel het aantal opeenvolgende bezoeken

Ik miste de mysql-tag en schreef deze oplossing op. Helaas dit werkt niet in MySQL omdat het geen vensterfuncties ondersteunt .

Ik post het toch, omdat ik er wat moeite voor heb gedaan. Getest met PostgreSQL. Zou op dezelfde manier werken met Oracle of SQL Server (of een ander fatsoenlijk RDBMS dat vensterfuncties ondersteunt).

Testopstelling

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

Eenvoudige versie

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

Uitgang:

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

Sneller/Korter

Later vond ik een nog betere manier. grp cijfers zijn niet continu (maar continu stijgend). Maakt niet uit, want die zijn slechts een middel om een ​​doel te bereiken:

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 voor beide.

Meer



  1. Kun je if-then-else-logica in SQL hebben?

  2. INSERT ... OP DUPLICATE KEY UPDATE met WAAR?

  3. OUTPUT-clausule in MySQL

  4. MySql PHP selecteer het aantal verschillende waarden van door komma's gescheiden gegevens (tags)