5

我有一个结构如下的 MySQL 表:

饮料日志(ID,用户 ID,饮料 ID,时间戳)

我正在尝试计算用户(ID 为 1)每天至少记录 5 次饮料(ID 为 1)的连续天数的最大值。我很确定这可以使用如下视图来完成:

CREATE or REPLACE VIEW daycounts AS
SELECT count(*) AS n, DATE(timestamp) AS d FROM beverages_log
WHERE users_id = '1' AND beverages_id = 1 GROUP BY d;

CREATE or REPLACE VIEW t AS SELECT * FROM daycounts WHERE n >= 5;

SELECT MAX(streak) AS current FROM ( SELECT DATEDIFF(MIN(c.d), a.d)+1 AS streak
FROM t AS a LEFT JOIN t AS b ON a.d = ADDDATE(b.d,1)
LEFT JOIN t AS c ON a.d <= c.d
LEFT JOIN t AS d ON c.d = ADDDATE(d.d,-1)
WHERE b.d IS NULL AND c.d IS NOT NULL AND d.d IS NULL GROUP BY a.d) allstreaks;

但是,每次运行此检查时重复为不同用户创建视图似乎效率很低。MySQL 中有没有一种方法可以在单个查询中执行此计算,而无需创建视图或多次重复调用相同的子查询?

4

3 回答 3

6

只要 users_id 和 Drinks_id 上有一个复合索引,这个解决方案似乎表现得很好 -

SELECT *
FROM (
    SELECT t.*, IF(@prev + INTERVAL 1 DAY = t.d, @c := @c + 1, @c := 1) AS streak, @prev := t.d
    FROM (
        SELECT DATE(timestamp) AS d, COUNT(*) AS n
        FROM beverages_log
        WHERE users_id = 1
        AND beverages_id = 1
        GROUP BY DATE(timestamp)
        HAVING COUNT(*) >= 5
    ) AS t
    INNER JOIN (SELECT @prev := NULL, @c := 1) AS vars
) AS t
ORDER BY streak DESC LIMIT 1;
于 2012-04-05T00:38:52.220 回答
0

为什么不在他们的 daycounts 视图中包含 user_id,并按 user_id 和日期分组。

还要在视图 t 中包含 user_id。

然后,当您反对 t 时,将 user_id 添加到 where 子句。

然后,您不必为每个用户重新创建视图,您只需要记住将其包含在 where 子句中。

于 2012-04-04T23:36:07.817 回答
0

这有点棘手。我将从按天总结事件的观点开始:

CREATE VIEW BView AS
    SELECT UserID, BevID, CAST(EventDateTime AS DATE) AS EventDate, COUNT(*) AS NumEvents
    FROM beverages_log
    GROUP BY UserID, BevID, CAST(EventDateTime AS DATE)

然后,我将使用 Dates 表(只是一个每天一行的表;非常方便)来检查所有可能的日期范围并丢弃任何有间隙的日期范围。这可能会很慢,但这是一个开始:

SELECT
    UserID, BevID, MAX(StreakLength) AS StreakLength
FROM
    (
    SELECT
        B1.UserID, B1.BevID, B1.EventDate AS StreakStart, DATEDIFF(DD, StartDate.Date, EndDate.Date) AS StreakLength
    FROM
        BView AS B1
        INNER JOIN Dates AS StartDate ON B1.EventDate = StartDate.Date
        INNER JOIN Dates AS EndDate ON EndDate.Date > StartDate.Date
    WHERE
            B1.NumEvents >= 5
        -- Exclude this potential streak if there's a day with no activity
        AND NOT EXISTS (SELECT * FROM Dates AS MissedDay WHERE MissedDay.Date > StartDate.Date AND MissedDay.Date <= EndDate.Date AND NOT EXISTS (SELECT * FROM BView AS B2 WHERE B1.UserID = B2.UserID AND B1.BevID = B2.BevID AND MissedDay.Date = B2.EventDate))
        -- Exclude this potential streak if there's a day with less than five events
        AND NOT EXISTS (SELECT * FROM BView AS B2 WHERE B1.UserID = B2.UserID AND B1.BevID = B2.BevID AND B2.EventDate > StartDate.Date AND B2.EventDate <= EndDate.Date AND B2.NumEvents < 5)
    ) AS X
GROUP BY
    UserID, BevID
于 2012-04-04T23:36:31.327 回答