sql >> Database >  >> RDS >> Mysql

Controleer op x opeenvolgende dagen - gegeven tijdstempels in database

U kunt dit bereiken met behulp van een verschoven self-outer-join in combinatie met een variabele. Zie deze oplossing:

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

Dit geeft een 1 . terug of een 0 op basis van of een gebruiker 30 opeenvolgende dagen of langer heeft ingelogd op ANYTIME in het verleden.

De dupe van deze query zit echt in de eerste subselect. Laten we eens nader kijken, zodat we beter kunnen begrijpen hoe dit werkt:

Met de volgende voorbeeldgegevensset:

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

Deze vraag:

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

Zal produceren:

Zoals je kunt zien, is wat we aan het doen zijn verschuiven de samengevoegde tafel met +1 dag. Voor elke dag die niet opeenvolgend is met de voorgaande dag, een NULL waarde wordt gegenereerd door de LEFT JOIN.

Nu we weten waar de niet-opeenvolgende dagen zijn, kunnen we een variabele gebruiken om elke set te onderscheiden van opeenvolgende dagen door te detecteren of de rijen van de verschoven tabel al dan niet NULL zijn . Als ze NULL zijn , de dagen zijn niet opeenvolgend, dus verhoog de variabele gewoon. Als ze NOT NULL zijn , verhoog de variabele dan niet:

Nadat we elke reeks opeenvolgende dagen hebben onderscheiden met de oplopende variabele, is het een kwestie van groeperen op elke "set" (zoals gedefinieerd in de consec_set kolom) en gebruik HAVING om elke set uit te filteren die minder dan de opgegeven opeenvolgende dagen heeft (30 in uw voorbeeld):

Tot slot sluiten we DAT . af zoek en tel eenvoudig het aantal sets met 30 of meer opeenvolgende dagen. Als er een of meer van deze sets waren, retourneer dan 1 , retourneer anders 0 .

Bekijk een SQLFiddle stapsgewijze demo



  1. Hoe een externe sleutelbeperking in SQL te verwijderen?

  2. QPSQL-stuurprogramma is niet geladen Qt

  3. Hoe u alle weergaven in Oracle Database kunt weergeven

  4. PostgreSQL-prestaties benchmarken met Sysbench