sql >> Databasteknik >  >> RDS >> Mysql

Räkna antalet på varandra följande besök

Jag missade mysql-taggen och skrev upp den här lösningen. Tyvärr, det här fungerar inte i MySQL eftersom det inte stöder fönsterfunktioner .

Jag lägger upp det ändå, eftersom jag ansträngt mig lite för det. Testad med PostgreSQL. Skulle fungera på liknande sätt med Oracle eller SQL Server (eller någon annan anständig RDBMS som stöder fönsterfunktioner).

Testa installationen

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

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

Utdata:

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

Snabbare/kortare

Jag hittade senare ett ännu bättre sätt. grp siffrorna är inte kontinuerliga (men stiger kontinuerligt). Spelar ingen roll, eftersom de bara är ett medel mot ett mål:

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 båda.

Mer



  1. hur sparar man taggar (sökord) i databasen?

  2. Välj datatyp för fältet i postgres

  3. VAR kontra HA

  4. Var ska jag lagra en databasanslutningssträng?