'MySQL Explode annual range to monthly rows using calendar table - issue when DAY doesn't exist in a given month

I have a MySQL db that I've created to manually create revenue reports every month using exported csv's from our subscription service. Every month, I need to take annual subscriptions and convert them into monthly mrr reports. I am creating a new table and adding a row for each month of the annual subscription (between a date_from and date_to field) by using a calendar table that has every day of the year for years in it along with items like LastDayOfMonth, LastDateOfMonth

I realized today that if an annual subscription falls on the 29th, 30th, or 31st, it's not creating a record for months that have fewer days. I'm trying to keep this in MySQL if possible.

Original table record looks like this:

customer_id | subscription_id | date_from | date_to | plan_id | mrr
123456789 | 123456789 | 2021-05-31 | 2022-05-30 | streaming-service | 200

I need to create the following rows in the new table (annual_customer_revenue)

customer_id | subscription_id | date_from | plan_id | mrr
123456789 | 123456789 | 2021-05-31  | streaming-service | 16.67
123456789 | 123456789 | 2021-06-30  | streaming-service | 16.67
123456789 | 123456789 | 2021-07-31  | streaming-service | 16.67
123456789 | 123456789 | 2021-08-31  | streaming-service | 16.67
123456789 | 123456789 | 2021-09-30  | streaming-service | 16.67
123456789 | 123456789 | 2021-10-31  | streaming-service | 16.67
123456789 | 123456789 | 2021-11-30  | streaming-service | 16.67
123456789 | 123456789 | 2021-12-31  | streaming-service | 16.67
123456789 | 123456789 | 2021-01-31  | streaming-service | 16.67
123456789 | 123456789 | 2021-02-28  | streaming-service | 16.67
123456789 | 123456789 | 2021-03-31  | streaming-service | 16.67
123456789 | 123456789 | 2021-04-30  | streaming-service | 16.67

My current MySQL INSERT looks like this:

INSERT INTO annual_customer_revenue (customer_id, subscription_id, date_from, plan_id, mrr)
SELECT ch.customer_id, ch.subscription_id, ct.TheDate, ch.plan_id, (ch.mrr/12) AS mrr 
FROM customer_history AS clh ,calendar_table AS ct  
WHERE ct.TheDate >= ch.date_from AND ct.TheDate <= ch.date_to AND DAY(ct.TheDate) = DAY(ch.date_from);

What I need to figure out is how to do something different when a month doesn't have a DAY that matches and use the LastDateOfMonth from my calendar table instead. Because I'm using DAY(ct.TheDate) = DAY(ch.date_from) in the WHERE clause, I'm not entirely sure how to do that.

I'm stuck on how to do this in one query vs. split it out into 2 where I handle the 29/30/31st on their own. Is this possible?



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source