Mysql
 sql >> Datenbank >  >> RDS >> Mysql

Suche nach x aufeinanderfolgenden Tagen - gegebene Zeitstempel in der Datenbank

Sie können dies erreichen, indem Sie einen verschobenen Self-Outer-Join in Verbindung mit einer Variablen verwenden. Siehe diese Lösung:

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

Dies gibt entweder eine 1 zurück oder eine 0 basierend darauf, ob sich ein Benutzer JEDERZEIT an 30 aufeinanderfolgenden Tagen oder länger angemeldet hat in der Vergangenheit.

Die Hauptlast dieser Abfrage liegt wirklich in der ersten Unterauswahl. Sehen wir uns das genauer an, damit wir besser verstehen, wie das funktioniert:

Mit folgendem Beispieldatensatz:

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

Diese Abfrage:

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

Wird produzieren:

Wie Sie sehen können, verschieben wir uns gerade den verbundenen Tisch um +1 Tag. Für jeden Tag, der nicht auf den vorherigen Tag folgt, ein NULL Wert wird durch den LEFT JOIN generiert.

Jetzt, wo wir wissen wo die nicht aufeinanderfolgenden Tage sind, können wir eine Variable verwenden, um jeden Satz zu unterscheiden von aufeinanderfolgenden Tagen, indem festgestellt wird, ob die Zeilen der verschobenen Tabelle NULL sind oder nicht . Wenn sie NULL sind , die Tage sind nicht aufeinander folgend, also erhöhen Sie einfach die Variable. Wenn sie NOT NULL sind , dann erhöhen Sie die Variable nicht:

Nachdem wir jeden Satz aufeinanderfolgender Tage mit der inkrementierenden Variable differenziert haben, ist es nur noch eine Frage der Gruppierung nach jedem "Satz" (wie in consec_set definiert). Spalte) und mit HAVING So filtern Sie jeden Satz heraus, der weniger als die angegebenen aufeinanderfolgenden Tage hat (in Ihrem Beispiel 30):

Dann schließlich wickeln wir DAS ein abfragen und einfach die Anzahl der Sätze zählen, die 30 oder mehr aufeinanderfolgende Tage hatten. Wenn es einen oder mehrere dieser Sätze gab, geben Sie 1 zurück , andernfalls gib 0 zurück .

Sehen Sie sich eine Schritt-für-Schritt-Demo von SQLFiddle an