Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Verifique por x dias consecutivos - timestamps fornecidos no banco de dados


Você pode fazer isso usando uma autojunção externa deslocada em conjunto com uma variável. Veja esta solução:
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

Isso retornará um 1 ou um 0 com base em se um usuário fez login por 30 dias consecutivos ou mais em ANYTIME no passado.

O peso dessa consulta está realmente na primeira subseleção. Vamos dar uma olhada mais de perto para entendermos melhor como isso funciona:

Com o seguinte conjunto de dados de exemplo:
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');

Esta consulta:
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

Vai produzir:



Como você pode ver, o que estamos fazendo é mudar a tabela unida por +1 dia. Para cada dia que não é consecutivo com o dia anterior, um NULL valor é gerado pelo LEFT JOIN.

Agora que sabemos onde estão os dias não consecutivos, podemos usar uma variável para diferenciar cada conjunto de dias consecutivos detectando se as linhas da tabela deslocada são ou não NULL . Se forem NULL , os dias não são consecutivos, então apenas incremente a variável. Se eles forem NOT NULL , então não incremente a variável:



Depois de diferenciarmos cada conjunto de dias consecutivos com a variável de incremento, basta agrupar por cada "conjunto" (conforme definido no consec_set coluna) e usando HAVING para filtrar qualquer conjunto que tenha menos do que os dias consecutivos especificados (30 no seu exemplo):



Então, finalmente, encerramos ISSO consulta e basta contar o número de conjuntos que tiveram 30 ou mais dias consecutivos. Se houver um ou mais desses conjuntos, retorne 1 , caso contrário, retorne 0 .

Veja uma demonstração passo a passo do SQLFiddle