MySQL: How to do daily reports? - php

Assume I have a log of something. Each record has a timestamp (MySQL data type TIMESTAMP) in the format date('Y-m-d H:i:s') (from PHP). I need to produce a report that looks like this:
===========================================
| Date | Total Sales |
===========================================
| Thursday, Dec 1, 2011 | 100 |
-------------------------------------------
| Friday, Dec 2, 2011 | 200 |
-------------------------------------------
| Saturday, Dec 3, 2011 | 150 |
-------------------------------------------
... and so on ...
I assume I have to dynamically build the SQL from PHP, which is OK. I'm just not sure what the SQL would look like. Ideas?

How to count the number of records per day
This is what I originally thought you wanted. I'm leaving it here because it might be useful to other people.
You have a bunch of timestamps. You want to group them by individual days and get the number of records per day.
Assuming your timestamp field is named ts, you can do something like this:
SELECT COUNT(*), DAY(ts), MONTH(ts), YEAR(ts) FROM tableName
GROUP BY YEAR(ts), MONTH(ts), DAY(ts);
How to generate daily reports for individual people
Ok this is what you really wanted. Let's draw a sample table with some records:
ts person sales
2011-12-01 10:00:00 John 10
2011-12-01 10:30:00 Mary 25
2011-12-01 11:00:00 John 20
2011-12-02 14:00:00 John 40
To get the daily totals for a particular person, you would do:
SELECT SUM(sales), DAY(ts), MONTH(ts), YEAR(ts) FROM tableName
WHERE person='John'
GROUP BY YEAR(ts), MONTH(ts), DAY(ts);
This is selecting records where person is John, grouped by unique days, and summing the sales value for those records. If you want reports for every person combined, just remove the WHERE clause.
Final note
You can simplify your SQL a little bit if you use the DATE type instead of the DATETIME type. I'm selecting and grouping by the day, month and year which I need to get using three separate functions. If you're using the DATE type, calling these functions would be unnecessary and I could just select and group by ts directly. It's up to you how you want to represent your data.

Related

Convert MySQL time stamp into DAY then sort by MAX

Trying to write a mySQL query that selects the most recent(**) number of times a light was turned on by a user.
**Most recent being all the times the light was turned on for the most recent day in the DB.
Sample Table:
DB Name: LLL
Table Name: Lights
UserID | LightOn | LightOff
-----------------------------------------------------
3 | 2018-01-08 09:00:00 | 2018-01-08 09:03:00
3 | 2018-01-08 10:15:00 | 2018-01-08 10:17:00
3 | 2018-01-07 15:00:00 | 2018-01-07 15:05:00
So, From this table, we can tell that
UserID 3 (Bob) turns the light on:
2 times on January 8th (at 9AM for 3 minutes and 10:15AM for 2 minutes) &
1 times on January 7th (at 3PM for 5 mins)
I want my query to return 2, because there are 2 records for the most recent day of January 8th.
I'm at the point where I can only get the number of records:
SELECT COUNT(C.LightOff) AS count FROM LLL.Lights AS C
WHERE C.UserID = 3
ORDER BY C.LightsOff DESC
I get the following back:
count
-------
3
I need to figure out a way to convert the time stamp into a DAY and get all the records that match that MAX Day.
The desired result is:
count
-------
2
Any ideas?
Assuming you have a proper datetime value in you lightoff column
you could get the most recent day and join with your count
select count(*) from LLL.Lights
inner join (
select max(date(LightOff)) max_date
FROM LLL.Lights
WHERE UserID = 3
) t on t.max_date = date(LightOff)

Solution for a MySQL query

I need a solution for my project..Actually, I got trapped in a problem..Let me show you the picture of my problem.
Senerio of my problem
If you can see, there are some columns of 12 months.So I need to run a MySQL query or a program which can show me the selected columns which contain the text "Not Paid".
For example.
If I want to check the members between January to March or June to November etc. who are "Not Paid"..so what is the solution for that...??
I know that we can use the "between" clause but I am not getting the idea..Because I want to use two drops down menu where the user can select from which month to which month they want to see.
Please help me to get rid out of this problem..I am a new beginner in PHP.
If I were you I would represent the months as dates, even if strings (e.g., '2017-06') and then map the month to the semantic representation string (e.g., 'June'). So one way is to store the date in your MySQL database as a varchar.
In terms of modeling the data in your database, I would create a table with three columns 1) the user_id; 2) the due_date; 3) the payment_status.
.----------------------------.
| 0 | 2017-06 | paid |
| 1 | 2017-07 | not paid |
| 2 | 2017-08 | paid |
| 3 | 2017-09 | paid |
'----------------------------'
Now you can write a query to retrieve the members between January to March who has "Not Paid". (note: this is just one way, there are many ways to query this)
SELECT
DISTINCT(user_id)
FROM payments_table
WHERE due_date >= '2017-03'
AND due_date < '2017-06'
AND payment_status = 'not paid';

Find out which months have data logged in MYSQL database?

In my database I have the date of each customer order stored in the format 02 Mar 2015
I have data from March and April and I want a query that will return just those 2 months. Once I have data for May it will return the three months etc.
The SQL syntax which I am trying to use is:
SELECT DISTINCT MONTH(DATE_FORMAT(date,'%d %b %y')) FROM orders
However this returns 0 rows. I presume this is an issue with date format.
EDIT:
Sample data from table:
id | date | time | order_id | item | quantity
1 | 02 Mar 2015 | 14:22 | 1029 | clasico | 9
1 | 05 Apr 2015 | 13:58 | 1029 | hindu | 10
try
SELECT DISTINCT MONTH(`order_date`) FROM `orders`
where order_date is the date field in orders
Note: The answer assumes the date column is a varchar rather than a datetime datatype.
The DATE_FORMAT() function is used to display date/time data in different formats. Note: This assumes the data is a datetime data type.
The STR_TO_DATE() returns a datetime value by taking a string and a specific format string as arguments. See the code below in action in the SQL Fiddle demo.
select DISTINCT MONTH(STR_TO_DATE(date, '%d %b %y')) from orders

SQL Infinite Calendar Pattern

I'm going to make a Mysql based calendar system where you can have repeating pattern for lets say every monday forever and ever. It must also cover static/once-only events. What I'm wondering about, is which solution would be most logical (and best) for me to use. I have four methods which I'm wondering to chose between.
Method #1
Make a function which accepts parameters from and to. This function would create a temporary table table which imports existing static schedule through INSERT ... SELECT. Afterward it would read of the pattern table and populate the temporary table through the peroid based on from and to.
This solution seems nice from the point of view that queries will be simplier to fetch data with and it works into infinity since you can just repopulate the table depending of which month you're loading. What I'm curious about is whenever this might be a laggy way to do it or not.
Method #2
Create and join given patterns through a subquery and JOIN with static calendar.
This seems to be rather annoying since the queries would be a lot more bigger and would probably not be good at all(?).
Method #3
Basicly just INSERT pattern for lets say one year ahead. Then I guess a cron job would repopulate to make it one year ahead always.
This is a simple way to do it, but it feels like a lot of unneeded data stored and it doesn't really give the infinity which I'm after.
Method #4 (Suggested by Veger)
If I understand correctly, this method would fetch the pattern from another query and creates events upon execution. It's similar to my thoughts regarding Method #1 in that way that I consider simple pattern to create several rows.
However if this would be implemented outside Mysql, I would loose some database functionality which I'm after.
I hope you guys understood my situation, and if you could suggest either given and argue why it's the best or give another solution.
Personally I like the Method #1 the most, but I'm curious if it's laggy to repopulate the calendar table each and every call.
I have built this kind of calendar before. I found the best way to do it is to approach it the way that crons are scheduled. So in the database, make a field for minute, hour, day of month, month, and day of week.
For an event every Friday in June and August at 10:00pm your entry would look like
Minute Hour DayOfMonth Month DayOfWeek
0 22 * 6,8 5
You could then have a field that flags it as a one time event which will ignore this information and just use the start date and duration. For events that repeat that end eventually (say every weekend for 3 months) you just need to add an end date field.
This will allow you to select it back easily and reduce the amount of data that needs to be stored. It simplifies your queries as well.
I don't think there is a need to create temporary tables. To select back the relevant events you would select them by the calendar view. If your calendar view is by the month, your select would look something like:
SELECT Events.*
FROM Events
WHERE (Month LIKE '%,'.$current_month.',%' OR Month = '*')
AND DATE(StartDate) >= "'.date('Y-m-d', $firstDayOfCurrentMonth).'"
AND DATE(EndDate) <= "'.date('Y-m-d', $lastDayOfCurrentMonth).'"
Obviously this should be in a prepared statement. It also assumes that you have a comma before and after the first and last value in the comma separated list of months (ie. ,2,4,6,). You could also create a Month table and a join table between the two if you would like. The rest can be parsed out by php when rendering your calendar.
If you show a weekly view of your calendar you could select in this way:
SELECT Events.*
FROM Events
WHERE (DayOfMonth IN ('.implode(',', $days_this_week).','*')
AND (Month LIKE '%,'.$current_month.',%' OR Month = '*'))
AND DATE(StartDate) >= "'.date('Y-m-d', $firstDayOfCurrentMonth).'"
AND DATE(EndDate) <= "'.date('Y-m-d', $lastDayOfCurrentMonth).'"
I haven't tested those queries so there maybe some messed up brackets or something. But that would be the general idea.
So you could either run a select for each day that you are displaying or you could select back everything for the view (month, week, etc) and loop over the events for each day.
I like Veger's solution best .. instead of populating multiple rows you can just populate the pattern. I suggest the crontab format .. it works so well anyway.
You can query all patterns for a given customer when they load the calendar and fill in events based on the pattern. Unless you have like thousands of patterns for a single user this should not be all that slow. It should also be faster than storing a large number of row events for long periods. You will have to select all patterns at once and do some preprocessing but once again, how many patterns do you expect per user? Even 1000 or so should be pretty fast.
I've had this idea since I was still programming in GW Basic ;-) though, back then, I took option #3 and that was it. Looking back at it, and also some of the other responses, this would be my current solution.
table structure
start (datetime)
stop (datetime, nullable)
interval_unit ([hour, day, week, month, year?])
interval_every (1 = every <unit>, 2 every two <units>, etc.)
type ([positive (default), negative]) - will explain later
Optional fields:
title
duration
The type field determines how the event is treated:
positive; normal treatment, it shows up in the calendar
negative; this event cancels out another (e.g. every Monday but not on the 14th)
helper query
This query will narrow down the events to show:
SELECT * FROM `events`
WHERE `start` >= :start AND (`stop` IS NULL OR `stop` < :stop)
Assuming you query a range by dates alone (no time component), the the value of :stop should be one day ahead of your range.
Now for the various events you wish to handle.
single event
start = '2012-06-15 09:00:00'
stop = '2012-06-15 09:00:00'
type = 'positive'
Event occurs once on 2012-06-15 at 9am
bounded repeating event
start = '2012-06-15 05:00:00'
interval_unit = 'day'
interval_every = 1
stop = '2012-06-22 05:00:00'
type = 'positive'
Events occur every day at 5am, starting on 2012-06-15; last event is on the 22nd
unbounded repeating event
start = '2012-06-15 13:00:00'
interval_unit = 'week'
interval_every = 2
stop = null
type = 'positive'
Events occur every two weeks at 1pm, starting on 2012-06-15
repeating event with exceptions
start = '2012-06-15 16:00:00'
interval_unit = 'week'
interval_every = 1
type = 'positive'
stop = null
start = '2012-06-22 16:00:00'
type = 'negative'
stop = '2012-06-22 16:00:00'
Events occur every week at 4pm, starting on 2012-06-22; but not on the 22nd
I would suggest something around the lines of this:
Split your Events table into 2 because there are clearly 2 different types recurring events and static events and depending on the type they will have different attributes.
Then for a given Event look-up you would run 2 queries, one against each Event Type. For the static events table you would defiantly need (at least) one datetime field so the lookup for a given month would simply use that feild in the conditions (where event_date > FirstDayOfTheMonth and event_date < LastDayOfTheMonth ). Same logic for a weekly/yearly view.
This result set would be combined with a second result set from the recurring events table. Possible attributes could be similar to crontab entries, using day of week/day of month as the 2 main variables. If you're looking at a monthly view,
select * from recurring_events where DayOfWeek in (1,2,3,4,5,6,7) or (DayOfMonth > 0 and DayOfMonth < #NumberOfDaysInThisMonth )
Again similar if for a weekly/yearly view. To make this even simpler to interface, use stored procedures with all the logic for determining 'which days of the week are found between date A and date B'.
Once you have both result sets, you could aggregate them together in the client then display them together. The adavantage to this is there will be no need for "mock/empty records" nor async cronjobs which pre-fill, the queries could easily happen on the fly and if performance actually degrades, add a caching layer, especially for a system of this nature a cache makes perfect sense.
I'm actually looking for something similar to this and my solution so far (on paper I didn't start to structure or code yet) stores in 2 tables:
the "events" would get the date of the first occurrence, the title and description (plus the auto-increment ID).
the "events_recursion" table would cross reference to the previous table (with an event_id field for instance) and could work in 2 possible ways:
2.A: store all the occurrences by date (i.e. one entry for every occurrence so 4 if yo want to save "every friday of this month" or 12 for "the 1st of every month in 2012")
2.B: or saving the interval (I would save it in seconds) from the date of the first event in a field + the date of the last occurrence (or end of recursion) in another field such as
ID: 2
EVENT_ID: 1
INTERVAL: 604800 (a week if I'm not mistaken)
END: 1356912000 (should be the end of this year)
Then when you open the php that shows the schedule it would check for the event still active in that month with a joint between the two tables.
The reason why I would use 2 tables cross-referenced instead of saving all in one tables just comes from the facts that my projects sees very crazy events such as "every fridays AND the 3rd monday of every month" (that in this case would be 1 entry in the events tables and 2 with same "event_id" field in the second table. BTW my projects is for music teachers that here got small work on strict schedules decided 3 or 6 months at a time and are a real mess).
But as I have said i haven't started yet so I'm looking forward to seeing your solution.
PS: please forgive (and forget) my english, first isn't my language and second it is pretty late night and I'm sleepy
Maybe check out some great ideas from MySQL Events
and some more:
http://phpmaster.com/working-with-mysql-events/?utm_source=rss&utm_medium=rss&utm_campaign=phpmaster-working-with-mysql-events
http://dev.mysql.com/doc/refman/5.5/en/create-event.html
the best solution depends on whether you want to favor standard compliance (RFC5545) or working exclusively within MySQL.
depend on on flexible your recurrence rule engine needs to be. If you want simple rules (every 1st of month or every January, ...) then the solutions offered above have been detailed at length.
However should you want your application to offer compatibility with existing standards (RFC5545) which involves much more complex rules you should have a look at this SO post when building a calendar app, should i store dates or recurrence rules in my database?
I would do it as I explained here. It will create an infinite calander:
PHP/MySQL: Model repeating events in a database but query for date ranges
The downside is that there will be some calculation during the query. If you need a high performance website, preloading the data will be the way to go. You dont even have to preload all the events in the calendar, to make it possible for easy changing the values in a single event. But it would be wise to store all dates from now till ....
Now using cached values does make it less infinite, but it will increase speed.
Copy of awnser for easy access:
I would create a tally table with just one col called id and fill that table with numbers from 0 to 500. Now we easily use that to make selections instead of using a while loop.
Id
-------------------------------------
0
1
2
etc...
Then i'd store the events in a table with Name as varchar, startdate as datetime and repeats as int
Name | StartDate | Repeats
-------------------------------------
Meeting | 2012-12-10 00:00:00 | 7
Lunch | 2012-12-10 00:00:00 | 1
Now we can use the tally table to select all dates between two dates by using:
SELECT DATE_ADD('2012-12-09 00:00:00',INTERVAL Id DAY) as showdate
FROM `tally`
WHERE (DATE_ADD('2012-12-09 00:00:00',INTERVAL Id DAY)<='2012-12-20 00:00:00')
ORDER BY Id ASC
ShowDate
-------------------------------------
2012-12-09 00:00:00
2012-12-10 00:00:00
2012-12-11 00:00:00
2012-12-12 00:00:00
2012-12-13 00:00:00
2012-12-14 00:00:00
2012-12-15 00:00:00
2012-12-16 00:00:00
2012-12-17 00:00:00
2012-12-18 00:00:00
2012-12-19 00:00:00
2012-12-20 00:00:00
Then we join this on the events table to calculate the difference between the startdate and the showdate. We devided the results of this by the repeats column and if the remainder is 0, we have match.
All combined becomes:
SELECT E.Id, E.Name, E.StartDate, E.Repeats, A.ShowDate, DATEDIFF(E.StartDate, A.ShowDate) AS diff
FROM events AS E, (
SELECT DATE_ADD('2012-12-09 00:00:00',INTERVAL Id DAY) as showdate
FROM `tally`
WHERE (DATE_ADD('2012-12-09 00:00:00',INTERVAL Id DAY)<='2012-12-20 00:00:00')
ORDER BY Id ASC
) a
WHERE MOD(DATEDIFF(E.StartDate, A.ShowDate), E.Repeats)=0
AND A.ShowDate>=E.StartDate
Which results in
Id | Name |StartDate | Repeats | ShowDate | diff
---------------------------------------------------------------------------------
1 | Meeting | 2012-12-10 00:00:00 | 7 | 2012-12-10 00:00:00 | 0
2 | Lunch | 2012-12-10 00:00:00 | 1 | 2012-12-10 00:00:00 | 0
2 | Lunch | 2012-12-10 00:00:00 | 1 | 2012-12-11 00:00:00 | -1
2 | Lunch | 2012-12-10 00:00:00 | 1 | 2012-12-12 00:00:00 | -2
2 | Lunch | 2012-12-10 00:00:00 | 1 | 2012-12-13 00:00:00 | -3
2 | Lunch | 2012-12-10 00:00:00 | 1 | 2012-12-14 00:00:00 | -4
2 | Lunch | 2012-12-10 00:00:00 | 1 | 2012-12-15 00:00:00 | -5
2 | Lunch | 2012-12-10 00:00:00 | 1 | 2012-12-16 00:00:00 | -6
1 | Meeting | 2012-12-10 00:00:00 | 7 | 2012-12-17 00:00:00 | -7
2 | Lunch | 2012-12-10 00:00:00 | 1 | 2012-12-17 00:00:00 | -7
2 | Lunch | 2012-12-10 00:00:00 | 1 | 2012-12-18 00:00:00 | -8
2 | Lunch | 2012-12-10 00:00:00 | 1 | 2012-12-19 00:00:00 | -9
2 | Lunch | 2012-12-10 00:00:00 | 1 | 2012-12-20 00:00:00 | -10
Now you could (and should!) speed things up. For instance by directly storing dates in a table so you can just select all dates directly instead of using a tally table with dateadd. Every thing you can cache and dont have to calculate again is good.

How to find exact seasons and days from a given date range?

I need this for rent a car price calculation. Cars prices are different according to seasons.
I have a season_dates table like this
id slug start end
1 low 2011-01-01 00:00:00 2011-04-30 00:00:00
2 mid 2011-05-01 00:00:00 2011-06-30 00:00:00
3 high 2011-07-01 00:00:00 2011-08-31 00:00:00
4 mid 2011-09-01 00:00:00 2011-10-31 00:00:00
5 low 2011-11-01 00:00:00 2011-12-31 00:00:00
Users selecting days, for example:
start_day 08/20 end_day 08/25
My query like that:
SELECT * from arac_donemler
where DATE_FORMAT(start, '%m/%d') <= '08/20'
and DATE_FORMAT(end, '%m/%d') >= '08/25'
This gives me high season that's correct.
But what I couldn't handle is: what if user selects a date range between 2 seasons?
For example from 20 August to 05 September.
This time I have to find that date ranges belongs to which seasons?
And I have to calculate how many days per each seasons?
For the example above,
high season ending at 31 August. So 31-20 = 11 days for high season, 5 days for mid season.
How can I provide this separation?
I hope I could explain it.
I tried so many things like join table inside but couldn't succeed it.
I'll let others chime in with the right way to do date comparisons in SQL (yours almost certainly kills indexing for the table), but for a start, you can get exactly the seasons that are relevant by
select * from arac_donemler
where end >= [arrival-date]
and start <= [departure-date]
Then you should do the rest of your processing (figure out how many days in each season and so forth) in the business logic instead of in the database query.
I would store all single days within a table.
This is a simple example.
create table dates (
id int not null auto_increment primary key,
pday date,
slug tinyint,
price int);
insert into dates (pday,slug,price)
values
('2011-01-01',1,10),
('2011-01-02',1,10),
('2011-01-03',2,20),
('2011-01-04',2,20),
('2011-01-05',2,20),
('2011-01-06',3,30),
('2011-01-07',3,30),
('2011-01-08',3,30);
select
concat(min(pday),'/',max(pday)) as period,
count(*) as days,
sum(price) as price_per_period
from dates
where pday between '2011-01-02' and '2011-01-07'
group by slug
+-----------------------+------+------------------+
| period | days | price_per_period |
+-----------------------+------+------------------+
| 2011-01-02/2011-01-02 | 1 | 10 |
| 2011-01-03/2011-01-05 | 3 | 60 |
| 2011-01-06/2011-01-07 | 2 | 60 |
+-----------------------+------+------------------+
3 rows in set (0.00 sec)
EDIT. Version with grandtotal
select
case
when slug is null then 'Total' else concat(min(pday),'/',max(pday)) end as period,
count(*) as days,
sum(price) as price_per_period
from dates
where pday between '2011-01-02' and '2011-01-07'
group by slug
with rollup;
+-----------------------+------+------------------+
| period | days | price_per_period |
+-----------------------+------+------------------+
| 2011-01-02/2011-01-02 | 1 | 10 |
| 2011-01-03/2011-01-05 | 3 | 60 |
| 2011-01-06/2011-01-07 | 2 | 60 |
| Total | 6 | 130 |
+-----------------------+------+------------------+
4 rows in set (0.00 sec)
edit. Stored procedure to populate table
delimiter $$
create procedure calendario(in anno int)
begin
declare i,ultimo int;
declare miadata date;
set i = 0;
select dayofyear(concat(anno,'-12-31')) into ultimo;
while i < ultimo do
select concat(anno,'-01-01') + interval i day into miadata;
insert into dates (pday) values (miadata);
set i = i + 1;
end while;
end $$
delimiter ;
call calendario(2011);
If you have a table RENTAL too (the real version would need a lot of other details in it):
CREATE TABLE Rental
(
start DATE NOT NULL,
end DATE NOT NULL
);
and you populate it with:
INSERT INTO rental VALUES('2011-08-20', '2011-09-05');
INSERT INTO rental VALUES('2011-08-20', '2011-08-25');
then this query produces a plausible result:
SELECT r.start AS r_start, r.end AS r_end,
s.start AS s_start, s.end AS s_end,
GREATEST(r.start, s.start) AS p_start,
LEAST(r.end, s.end) AS p_end,
DATEDIFF(LEAST(r.end, s.end), GREATEST(r.start, s.start)) + 1 AS days,
s.id, s.slug
FROM rental AS r
JOIN season_dates AS s ON r.start <= s.end AND r.end >= s.start;
It yields:
r_start r_end s_start s_end p_start p_end days id slug
2011-08-20 2011-09-05 2011-07-01 2011-08-31 2011-08-20 2011-08-31 12 3 high
2011-08-20 2011-09-05 2011-09-01 2011-10-31 2011-09-01 2011-09-05 5 4 mid
2011-08-20 2011-08-25 2011-07-01 2011-08-31 2011-08-20 2011-08-25 6 3 high
Note that I'm counting 12 days instead of 11; that's the +1 in the days expression. It gets tricky; you have to decide whether if the car is returned on the same day as it is rented, is that one day's rental? What if it is returned the next day? Maybe the time matters? But that gets into detailed business rules rather than general principles. Maybe the duration is the larger of the raw DATEDIFF() and 1? Also note that there is only the rental start and end dates in this schema to identify the rental; a real schema would have some sort of Rental Agreement Number in the rental table.
(Confession: simulated using IBM Informix 11.70.FC2 on MacOS X 10.7.1, but MySQL is documented as supporting LEAST, GREATEST, and DATEDIFF and I simulated those in Informix. The most noticeable difference might be that Informix has a DATE type without any time component, so there are no times needed or displayed.)
But [...] seasons period always same every year. So I thought to compare only days and months. 2011 isn't important. Next years just 2011 will be used. This time problem occurs. For example low season includes November, December and then go to January, February, March, April. If a user selects a date range 01.05.2011 to ...2011 There is no problem. I just compare month and day with DATE_FORMAT(end, '%m/%d'). But if he chooses a range from December to next year January, how am I gonna calculate days?
Notice that 5 entries per year in the Season_Dates table is not going to make an 8" floppy disk break sweat over storage capacity for a good few years, let alone a 500 GiB monster disk. So, by far the simplest thing is to define the entries for 2012 in 5 new rows in the Season_Dates table. That also allows you to handle the fact that in December, the powers-that-be decide the rules will be different (20th December to 4th January will be 'mid', not 'low' season, for example).

Categories