sql >> Databasteknik >  >> RDS >> Mysql

Kontrollera för x dagar i följd - givna tidsstämplar i databasen

Du kan åstadkomma detta genom att använda en förskjuten själv-yttre-join i kombination med en variabel. Se den här lösningen:

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

Detta returnerar antingen en 1 eller en 0 baserat på om en användare har loggat in i 30 dagar i följd eller mer NÅR som helst förr.

Största delen av den här frågan ligger egentligen i det första undervalet. Låt oss ta en närmare titt så att vi bättre kan förstå hur detta fungerar:

Med följande exempeldatauppsättning:

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

Denna fråga:

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

Kommer att producera:

Som du kan se är det vi gör att skifta den sammanfogade tabellen med +1 dag. För varje dag som inte följer föregående dag, en NULL värdet genereras av LEFT JOIN.

Nu när vi vet där dagarna inte är på varandra följande kan vi använda en variabel för att skilja varje uppsättning av på varandra följande dagar genom att detektera om raderna i den flyttade tabellen är NULL . Om de är NULL , dagarna är inte på varandra följande, så det är bara att öka variabeln. Om de är NOT NULL , öka sedan inte variabeln:

Efter att vi har differentierat varje uppsättning av på varandra följande dagar med den ökande variabeln, är det bara en enkel fråga att gruppera efter varje "uppsättning" (enligt definitionen i consec_set kolumn) och med HAVING för att filtrera bort alla uppsättningar som har mindre än de angivna på varandra följande dagarna (30 i ditt exempel):

Sedan avslutar vi DET fråga och räkna helt enkelt antalet uppsättningar som hade 30 eller fler dagar i följd. Om det fanns en eller flera av dessa uppsättningar, returnera 1 , annars returnerar du 0 .

Se en SQLFiddle steg-för-steg-demo



  1. SQL Server Operativsystem fel 5:5 (Åtkomst nekas.)

  2. Varför ingen utdata när PLSQL Anonymous-blockeringen är klar?

  3. Hur får man tillgång till Oracle-databasen över nätverket?

  4. JSON_INSERT() – Infoga värden i ett JSON-dokument i MySQL