sql >> Database teknologi >  >> RDS >> Mysql

Tjek for x på hinanden følgende dage - givet tidsstempler i databasen

Du kan opnå dette ved at bruge en forskudt selv-ydre-join i forbindelse med en variabel. Se denne løsning:

SELECT IF(COUNT(1) > 0, 1, 0) AS has_consec
FROM
(
    SELECT *
    FROM
    (
        SELECT IF(b.login_date IS NULL, @val:[email protected]+1, @val) AS consec_set
        FROM tbl a
        CROSS JOIN (SELECT @val:=0) var_init
        LEFT JOIN tbl b ON 
            a.user_id = b.user_id AND
            a.login_date = b.login_date + INTERVAL 1 DAY
        WHERE a.user_id = 1
    ) a
    GROUP BY a.consec_set
    HAVING COUNT(1) >= 30
) a

Dette returnerer enten en 1 eller en 0 baseret på, om en bruger har logget ind i 30 på hinanden følgende dage eller mere på ANYTIME i fortiden.

Hovedparten af ​​denne forespørgsel er virkelig i det første undervalg. Lad os se nærmere, så vi bedre kan forstå, hvordan dette fungerer:

Med følgende eksempeldatasæt:

CREATE TABLE tbl (
  user_id INT,
  login_date DATE
);

INSERT INTO tbl VALUES
(1, '2012-04-01'),  (2, '2012-04-02'),
(1, '2012-04-25'),  (2, '2012-04-03'),
(1, '2012-05-03'),  (2, '2012-04-04'),
(1, '2012-05-04'),  (2, '2012-05-04'),
(1, '2012-05-05'),  (2, '2012-05-06'),
(1, '2012-05-06'),  (2, '2012-05-08'),
(1, '2012-05-07'),  (2, '2012-05-09'),
(1, '2012-05-09'),  (2, '2012-05-11'),
(1, '2012-05-10'),  (2, '2012-05-17'),
(1, '2012-05-11'),  (2, '2012-05-18'),
(1, '2012-05-12'),  (2, '2012-05-19'),
(1, '2012-05-16'),  (2, '2012-05-20'),
(1, '2012-05-19'),  (2, '2012-05-21'),
(1, '2012-05-20'),  (2, '2012-05-22'),
(1, '2012-05-21'),  (2, '2012-05-25'),
(1, '2012-05-22'),  (2, '2012-05-26'),
(1, '2012-05-25'),  (2, '2012-05-27'),
                    (2, '2012-05-28'),
                    (2, '2012-05-29'),
                    (2, '2012-05-30'),
                    (2, '2012-05-31'),
                    (2, '2012-06-01'),
                    (2, '2012-06-02');

Denne forespørgsel:

SELECT a.*, b.*, IF(b.login_date IS NULL, @val:[email protected]+1, @val) AS consec_set
FROM tbl a
CROSS JOIN (SELECT @val:=0) var_init
LEFT JOIN tbl b ON 
    a.user_id = b.user_id AND
    a.login_date = b.login_date + INTERVAL 1 DAY
WHERE a.user_id = 1

Vil producere:

Som du kan se, er det, vi gør, at skifte den samlede tabel med +1 dag. For hver dag, der ikke er fortløbende med den foregående dag, en NULL værdi genereres af LEFT JOIN.

Nu hvor vi ved det hvor de ikke-på hinanden følgende dage er, kan vi bruge en variabel til at differentiere hvert sæt af på hinanden følgende dage ved at detektere, om den flyttede tabels rækker er NULL . Hvis de er NULL , dagene er ikke fortløbende, så du skal bare øge variablen. Hvis de er NOT NULL , så skal du ikke øge variablen:

Efter at vi har differentieret hvert sæt af på hinanden følgende dage med den inkrementerende variabel, er det så bare et simpelt spørgsmål om at gruppere efter hvert "sæt" (som defineret i consec_set kolonne) og ved hjælp af HAVING for at bortfiltrere ethvert sæt, der har mindre end de angivne på hinanden følgende dage (30 i dit eksempel):

Så afslutter vi DET forespørg og tæl simpelthen antallet af sæt, der havde 30 eller flere på hinanden følgende dage. Hvis der var et eller flere af disse sæt, så returner 1 , ellers returner 0 .

Se en SQLFiddle trin-for-trin demo



  1. Brug IDENT_CURRENT() til at returnere den aktuelle identitetsværdi på en identitetskolonne i SQL Server

  2. tsql returnerer en tabel fra en funktion eller butiksprocedure

  3. Deadlocks i PostgreSQL, når du kører UPDATE

  4. Kan ikke indsætte eksplicit værdi for identitetskolonnen i tabellen "tabel", når IDENTITY_INSERT er indstillet til FRA