Mysql
 sql >> Base de données >  >> RDS >> Mysql

Vérifier x jours consécutifs - horodatages donnés dans la base de données

Vous pouvez accomplir cela en utilisant une jointure auto-externe décalée en conjonction avec une variable. Voir cette solution :

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

Cela renverra soit un 1 ou un 0 basé sur si un utilisateur s'est connecté pendant 30 jours consécutifs ou plus à À TOUT MOMENT autrefois.

Le poids de cette requête est vraiment dans la première sous-sélection. Examinons de plus près afin de mieux comprendre comment cela fonctionne :

Avec l'exemple de jeu de données suivant :

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

Cette requête :

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

Produira :

Comme vous pouvez le voir, ce que nous faisons est changeant la table jointe par +1 jour. Pour chaque jour non consécutif au jour précédent, un NULL La valeur est générée par LEFT JOIN.

Maintenant que nous savons où les jours non consécutifs sont, nous pouvons utiliser une variable pour différencier chaque ensemble de jours consécutifs en détectant si oui ou non les lignes de la table décalée sont NULL . S'ils sont NULL , les jours ne sont pas consécutifs, il suffit donc d'incrémenter la variable. S'ils sont NOT NULL , alors n'incrémentez pas la variable :

Après avoir différencié chaque ensemble de jours consécutifs avec la variable d'incrémentation, il s'agit simplement de regrouper par chaque "ensemble" (tel que défini dans le consec_set colonne) et en utilisant HAVING pour filtrer tout ensemble qui a moins que les jours consécutifs spécifiés (30 dans votre exemple) :

Puis enfin, nous emballons CELA interrogez et comptez simplement le nombre d'ensembles qui ont eu 30 jours consécutifs ou plus. S'il y avait un ou plusieurs de ces ensembles, alors retournez 1 , sinon renvoie 0 .

Voir une démo pas à pas de SQLFiddle