Forum Moderators: coopster
Calendars always confuse me. LOL.
Ok, I have coded a calendar which displays all the days for each month ok. They are shown as boxes, ie 1-31. Now I have events posted that last for a duration of days.
Example:
Start = 1 january
End = 5 January
So, whats the sql query to span the days and show a link for each of those days? 1-5? Bearing in mind that multiple events could be posted, so it needs to span for all events. I`m sure its something simple, but I keep on scratching my head. At this rate I`ll be bald.
If its more complicated that I first thought, I`ll just forget it and have just a start day.
Cheers,
dc
eventID startDate endDate
0000001 2006-01-01 2006-01-05
0000002 2006-02-12 2006-02-12
0000003 2006-02-12 2006-02-15
Jan 1
Jan 2
Jan 3
Jan 4
Jan 5
Feb 12 (two links on this date)
Feb 13
Feb 14
Feb 15
Not quite. I want the display to span days. So using your example:
3 events. First spans 5 days, 2nd no days, 3rd 3 days...
eventID startDate endDate
0000001 2006-01-01 2006-01-05
0000002 2006-02-12 2006-02-12
0000003 2006-02-12 2006-02-15
This would be..
Jan 1 (link for event 0000001)
Jan 2 (link for event 0000001)
Jan 3 (link for event 0000001)
Jan 4 (link for event 0000001)
Jan 5 (link for event 0000001)
Feb 12 (link for event 0000002 && link for event 0000003)
Feb 13 (link for event 0000003)
Feb 14 (link for event 0000003)
Feb 15 (link for event 0000003)
Hope that makes some sense. So, basically a link on all days the the event or events span. I can easily loop and show the event for the start date or the end date on their own, but the span has me confused
dc
So first you are going to
WHERE MONTH(startDate) = $thisMonth OR MONTH(endDate) = $thisMonth
$month = 3;
$year = 2006;
$first = 1;
// last day in month for year
$last = date [php.net]('t', mktime [php.net](0, 0, 0, $month+1, 0, $year));
$stmt = "
SELECT
eventID,
startDate,
endDate,
CASE MONTH(startDate)
WHEN $month THEN DAY(startDate)
ELSE $first
END
¦¦ ',' ¦¦
CASE MONTH(endDate)
WHEN $month THEN DAY(endDate)
ELSE $last
END AS
range
FROM eventsTable
WHERE MONTH(startDate) = $month OR MONTH(endDate) = $month";
$eventDays = array();
$rows = mysql_query($stmt);
while ($row = mysql_fetch_array($rows)) {
list($lo, $hi) = explode(',', $row['range']);
foreach (range($lo, $hi) as $day) {
$eventDays[$day] = $day;
}
}
ksort [php.net]($eventDays);
print '<pre>';
print_r($eventDays);
print'</pre>';
Anyway, I have an event set as follows:
Start: '2006-03-01'
End: '2006-03-05'
When I ran your code the array it gave me was as follows:
Array
(
[0] => 0
[1] => 1
)
Not quite sure what thats telling me.
dc
The CASE logic is basically building a range of the days for each row selected for the month and year you are specifying (I did not include the year in the WHERE clause of the query, you will want to add that). So, if you had one row like the example you specified here it would return:
eventID startDate endDate range
0000001 2006-03-01 2006-03-05 1,5
Array
(
[1] => 1
[2] => 2
[3] => 3
[4] => 4
[5] => 5
)
So, I now have two events in the system, both in March, one spanning 5 days, the other spanning 4. If I echo the dates thats ok:
from: 2006-03-01
to: 2006-03-05
from: 2006-03-03
to: 2006-03-07
but the range is just 1, whether I have one event or more. So, not returning 1,5 as expected.
SELECT id, fromDate, toDate, CASE MONTH(fromDate) WHEN 3 THEN DAY(fromDate) ELSE 1 END ¦¦ ',' ¦¦ CASE MONTH(toDate) WHEN 3 THEN DAY(toDate) ELSE 31 END AS range FROM mgr_events WHERE MONTH(fromDate) = 3 OR MONTH(toDate) = 3Array
(
[0] => 0
[1] => 1
)
Try running that dumped stmt from the command line to see what the returned rows look like.
I have no idea how to do that, sorry.
What database/version are you using?
localhost = 5.0.15-nt.
Production servers = MySQL 4.0.25-standard
Your date columns are of type DATE, correct?
Yes.
dc
so take that query that you dumped and login to mysql via the command line. Then see what that query returns that way.
Most hosts give you command line access, if not you could probably use that query in phpmyadmin or whatever you use to manage your db.
Try running that dumped stmt from the command line to see what the returned rows look like.I have no idea how to do that, sorry.
No problem, that's just how I usually do it to see the raw data returned by and SQL query. I cut and paste the stmt from the browser as you did here and then paste it into a
mysql>command line interface to see what the database returns to me without having to use PHP to parse and display the returned data. Another way to do so is to dump the $row data in a while loop:
print '<pre>';
while ($row = mysql_fetch_array($rows)) {
print_r($row);
}
exit('</pre>');
Array
(
[0] => 2
[id] => 2
[1] => 2006-03-01
[fromDate] => 2006-03-01
[2] => 2006-03-05
[toDate] => 2006-03-05
[3] => 1
[range] => 1
)
Array
(
[0] => 3
[id] => 3
[1] => 2006-03-03
[fromDate] => 2006-03-03
[2] => 2006-03-07
[toDate] => 2006-03-07
[3] => 1
[range] => 1
)
dc
All looks fine except our 'range' -- it looks like it is returning a boolean TRUE or something. I am using a standard SQL concatentation there but I wonder if the concatenation operators aren't being read as logical OR's or something. I would expect to see at least the comma separator there ...
$stmt = "
SELECT
eventID,
startDate,
endDate,
CONCAT(
CASE MONTH(startDate)
WHEN $month THEN DAY(startDate)
ELSE $first
END,
',',
CASE MONTH(endDate)
WHEN $month THEN DAY(endDate)
ELSE $last
END
) AS
range
FROM eventsTable
WHERE
YEAR(startDate) = $year AND MONTH(startDate) = $month
OR
YEAR(startDate) = $year AND MONTH(endDate) = $month"
;
MySQL Extensions to Standard SQL [dev.mysql.com]
I don't like that, I like developing in standards (purist?) so I modified my Server SQL Mode [dev.mysql.com] to change the
PIPES_AS_CONCATdirective.
I know this is it. Use CONCAT and you should be good to go.
I have the calendar returning the correct range now, but I`ve noticed that if I set a date as:
Start: Feb 26
End: April 2
It only shows links for April 1 & 2. I don`t mind it ignoring February as thats in the past, just wondering why its not spanning the whole of March too? If I specify a range for a single month it works fine.
Many thanks,
dc
WHERE
YEAR(startDate) = $year AND MONTH(startDate) = $month
OR
YEAR(startDate) = $year AND MONTH(endDate) = $month"
;
WHERE
YEAR(startDate) <= $year AND MONTH(startDate) <= $month
AND
YEAR(endDate) >= $year AND MONTH(endDate) >= $month
eventID startDate endDate range
0000001 2006-01-01 2006-12-31 1,31
0000001 2006-02-28 2006-04-01 1,31
0000001 2006-03-01 2006-03-02 1,2
I have two problems. The first is the query is still failing on a production server.
You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near '(fromDate) ELSE 01 END, ',',
Fine on localhost using the later sql version.
The second is the span. Let me explain more clearly. Lets say I have this event in the system:
Start: 25 Feb
End: 2 April
Now I would expect to see the event for the 25,26,27,28 Feb, ALL of March and then 1, 2 April.
Whats happening is the event is spanning ALL of Feb, ALL of March, ALL of April, ALL of May etc...ALL events in the system are spanning ALL months. Even Christmas which I have set in December is showing up in March. LOL.
As it is at the moment its spanning great for the current month. So, do we need another query for the previous month and then one for the next?
Hope that made some sense. Still not sure why the query is failing on MySQL 4.0.25 Standard.
David.
Can`t understand why its working for March, but then not for other months if I go back or forward.
dc
Before MySQL 4.1, the type of the return value (INTEGER, DOUBLE, or STRING) is the same as the type of the first returned value (the expression after the first THEN). From MySQL 4.1.0, the default return type is the compatible aggregated type of all return values.Note that CASE evaluation depends also on the context in which it is used. If used in string context, the result is returned as a string. If used in numeric context, the result is returned decimal, real, or integer value.
I'm trying to put this all together in my head to figure out the difference and what you may have to do to get it to work correctly.
BTW, I looked at the column name being used --> 'range' and in 5.1 it is a reserved word, dc. Rename that column alias to something else, 'daysRange' perhaps.
We may be missing in communication here. Have a closer look at the rows being returned in message #24 ... posted again here:
eventID startDate endDate daysRange
0000001 2006-01-01 2006-12-31 1,31
0000001 2006-02-28 2006-04-01 1,31
0000001 2006-03-01 2006-03-02 1,2
We aren't so much concerned with which rows are being returned here as we are with the range of days telling us to *mark* as having events. Eventually we could add a DISTINCT keyword to our query and specify only the 'daysRange' column to be returned:
daysRange
1,31
1,2
whileloop since we know we really don't need to continue building the array of eventDays.
while ($row = mysql_fetch_array($rows)) {
list($lo, $hi) = explode(',', $row['range']);
foreach (range($lo, $hi) as $day) {
$eventDays[$day] = $day;
}
// Is this a full date range?
if ($lo == $first && $hi == $last) {
break;
}
} I'm getting ahead though. First, let's stick with bridging the possible communication gap in regards to the current result set being returned and the how/why of it. Remember that the assumption is we are building for only one calendar month at a time and the possibility of an event on any given day within that month.
Hopefully I haven't misunderstood the desired return!
I understand now about the date range. I just can`t wrap my head around the syntax I need to pull the desired results. I`ve tried loops within loops and the hole just keeps getting bigger and bigger. And there`s still this problem of the syntax not working on the production server.
Grr..:)