Search code examples
mysqlsqlsubquerygaps-and-islands

How can i run this query for each user without using any function or procedure?


Here is my query which returns streaks for specific user=27:

SELECT MAX(sum) AS streak
FROM (
  SELECT
    game_date,
    IF(points > 0, @sum:=@sum+1, @sum:=0) AS sum
  FROM (
    SELECT
      game_date,
      (SELECT COUNT(*) FROM point WHERE user_id = 27 AND bet_id = b.id AND goals > 0) AS points
    FROM bet b
    WHERE game_date < NOW()
    ORDER BY game_date DESC
  ) t1, (SELECT @sum:=0) t2
) t;

What i try: it runs successfully on my local MySQL but gives an error on live phpMyAdmin that user_id is an unknown column in the where clause.

SELECT DISTINCT user_id,(
SELECT MAX(SUM) AS streak
FROM (
  SELECT
    game_date,
    IF(points > 0, @sum:=@sum+1, @sum:=0) AS SUM
  FROM (
    SELECT
      game_date,
      (SELECT COUNT(*) FROM POINT WHERE user_id = p.user_id AND bet_id = b.id AND goals > 0) AS points
    FROM bet b
    WHERE game_date < NOW()
    ORDER BY game_date DESC
  ) t1, (SELECT @sum:=0) t2
) t) AS streak FROM POINT p;

Solution

  • Without any sample data to test against, I may be way off the mark but give this a try:

    SELECT user_id, MAX(SUM) AS streak
    FROM (
        SELECT b.game_date, u.user_id,
            IF(
                IFNULL(p.bet_id, 0) > 0,
                IF(@prev_user = u.user_id, @sum := @sum + 1, @sum := 1),
                @sum := 0
            ) AS SUM,
            @prev_user := u.user_id
        FROM bet b
        CROSS JOIN (SELECT DISTINCT user_id FROM `POINT`) u
        LEFT JOIN `POINT` p ON b.id = p.bet_id AND u.user_id = p.user_id AND p.goals > 0
        JOIN (SELECT @sum:=0, @prev_user:=0) vars
        WHERE b.game_date < NOW()
        ORDER BY u.user_id ASC, b.game_date DESC
    ) t
    GROUP BY user_id;
    

    This is a variation of the gaps-and-islands problem.This query takes a different ("modern") approach, using the ROW_NUMBER() window function -

    SELECT user_id, IFNULL(MAX(streak), 0)
    FROM (
        SELECT *, rn1 - rn2,
            IF (
                goals IS NOT NULL,
                ROW_NUMBER() OVER (PARTITION BY user_id, rn1 - rn2 ORDER BY game_date),
                NULL
            ) AS streak
        FROM (
            SELECT b.id AS bet_id, b.game_date, u.user_id, p.goals,
                ROW_NUMBER() OVER (ORDER BY u.user_id, game_date) rn1,
                ROW_NUMBER() OVER (PARTITION BY u.user_id, IF(p.bet_id IS NULL, 0, 1) ORDER BY game_date) rn2
            FROM bet b
            CROSS JOIN (SELECT DISTINCT user_id FROM `POINT`) u
            LEFT JOIN `POINT` p ON b.id = p.bet_id AND u.user_id = p.user_id AND p.goals > 0
            WHERE b.game_date < NOW()
        ) t1
    ) t2
    GROUP BY user_id;