Search code examples
sqlmysqlrecursive-query

Count all rows between two days. Return 0 if no rows are between the two dates


I am trying to fetch data, to show the user a overview of how many session the person have had. The table is structured in a similar way:

session_id User start_time
1 User A 2022-01-01
2 User A 2023-01-02
3 User A 2023-03-15
4 User A 2023-03-16
5 User A 2023-03-02
6 User A 2023-03-10

Is it possible to construct a query, where i get the count of session for each month, starting from the first session the user have had, until the current month. The output i would like, could be similar to the example below.

Month COUNT(session_id)
Jan-22 1
Feb-22 0
...-22 0
Dec-22 0
Jan-23 1
Feb-23 0
Mar-23 4
Apr-23 0

I have tried to create the following query:

SELECT DATE_FORMAT(DATE_ADD(min_month, INTERVAL m.n MONTH), '%Y-%m') as month,
       COALESCE(COUNT(c.consumption), 0) as total_consumption
FROM
  (SELECT DATE_FORMAT(MIN(start_time), '%Y-%m-01') as min_month
   FROM session
   WHERE user_id = 'sGcO42wMx1ZHXwZpZH3KWwWXzd13') as start_month
CROSS JOIN
  (SELECT n
   FROM (SELECT 0 n UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4
         UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) as n) as m
LEFT JOIN session as c ON DATE_FORMAT(c.start_time, '%Y-%m') = DATE_FORMAT(DATE_ADD(start_month.min_month, INTERVAL m.n MONTH), '%Y-%m')
   AND c.user_id = 'sGcO42wMx1ZHXwZpZH3KWwWXzd13'
GROUP BY month
ORDER BY month ASC;

But the result comming back only include 10 months:

Month COUNT(session_id)
Jan-22 1
...-22 0
Oct-22 0

Solution

  • With help of this result from StackExchange: Generate Dates between Date Ranges in mysql was i able to figure out how to get the right result.

    By creating a recursive table named Date_Ranges, i was able to create table based on the first session a user had, until the current day. With this i made a LEFT JOIN to, get the number of session from the original table.

    The expected query therefore was:

    WITH recursive Date_Ranges AS (
        SELECT (SELECT start_time FROM user.session WHERE user_id = 'sGcO42wMx1ZHXwZpZH3KWwWXzd13' ORDER BY start_time ASC LIMIT 1) AS d
        UNION ALL
        SELECT d + interval 1 month
        FROM Date_Ranges
        WHERE d < NOW()
    )
    SELECT DATE_FORMAT(dates.d, '%b-%y') AS Month, COUNT(s.session_id) AS Count
    FROM Date_Ranges dates
    LEFT JOIN user.session s ON DATE_FORMAT(s.start_time, '%Y-%m') = DATE_FORMAT(dates.d, '%Y-%m') AND s.user_id = 'sGcO42wMx1ZHXwZpZH3KWwWXzd13'
    GROUP BY Month;
    

    And produced the table response i wanted.