sql php confuse complex - php

PHP code (relevant)
$db = '2011-02-28'; $a = '2011-02-01';
and this is the part of the query
LEFT JOIN
abc
ON
abc.date between '$a' and '$db'
This query show following results :
1 2011-02-08
6 2011-02-09
6 2011-02-11
1 2011-02-13
but what i want is to get 0 as a result if there's n rows for other dates.

You can't do that (not without joining a table containing all dates, at least).
Displaying the 0s for dates with no data is the job of your application, not the database. It's trivial, just write a loop from the start date to the end date, and output the 0 for those dates with no rows in your result set.
SQL's job is to tell you what data is there, not what isn't.
while ($row = mysql_fetch_array($result)) {
$results[$row['date']] = $row['count'];
}
for ($time = strtotime('2011-02-01'); $time <= strtotime('2011-02-28'); $time += 86400) {
$datestr = date('Y-m-d', $time);
if (isset($results[$datestr])) {
echo "Count for date $datestr: " . $results[$datestr];
} else {
echo "Count for date $datestr: 0";
}
}

From the limited information you give about what problem you are trying to solve I would guess you are trying to find "free appointments" or similar.
If so then this should help you out. http://www.artfulsoftware.com/infotree/queries.php?&bw=1280#98
This website has a number of query patterns that will help your thinking and design of your database.

Related

Row highlighting based on due date using php

I'm relatively new to PHP, I've learned procedural PHP and I'm playing around with an app.
I've Looked around the site and found a few samples on date formatting and highlighting using PHP, HTML and CSS.
With the help found here and the PHP manual, I've put together some code to highlight 2 different rows among many others provided by a database that follow this criteria:
Anything due in 1 day (today, yesterday, last week, etc.) should color the table row red.
Anything 3 days out (between 1 day and 3 days in the future) should color the table row yellow.
Anything else should utilize the bootstrap "table-striped" styling.
Here is the code I've put together
//Today + 1 day
$oneDay = date('m/d/Y', strtotime("+1 day"));
//Today + 3 days
$threeDays = date('m/d/Y', strtotime("+3 days"));
//Database entry for comparison
$due = date_create($record['projected_delivery']);
$dueOneDay = date_create($oneDay);
$dueThreeDays = date_create($threeDays);
//Get the difference between the 2 dates
$diffOne = date_diff($due, $dueOneDay);
$diffThree = date_diff($due, $dueThreeDays);
if ($diffThree->format('%d') < 3) {
$highlight_css = " warning";
}elseif ($diffOne->format('%d') <= 1){
$highlight_css = " danger";
}else {
$highlight_css = "";
}
I then add the $highlight_css to the HTML.
So far some of the functionality is working. Proper highlighting is not added for:
Dates older than 1 day (i.e. yesterday, last week)
How can this functionality be achieved?
I would use this instead:
<?php
$dueDates =["06/05/2017","06/06/2017","06/07/2017","06/08/2017","06/09/2017","06/10/2017","06/11/2017","06/12/2017"];
$table = "<table class='table table-bordered'><thead><tr><th>Date</th></tr></thead><tbody>";
$today = date_create(date('m/d/Y')); // example: 06/07/2017
foreach ($dueDates as $dueStr) {
$due = date_create($dueStr);
$diff = date_diff($today, $due)->format("%r%a");
$highlight_css = "";
if ($diff > 1 && $diff <= 3) {
$highlight_css = " warning";
} elseif ($diff == 1) {
$highlight_css = " danger";
}
$table .= "<tr class='$highlight_css'><td>$dueStr</td></tr>";
}
$table .= "</tbody></table>";
?>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" >
<?php
echo $table;
$diffThree->format('%d')
will return a string instead of an integer. In order to compare it correctly with other numbers (3 and 1 in your case) you need to convert it to an int:
(int) $diffThree->format('%d')
and
(int) $diffOne->format('%d')
The issue
A DateInterval (the return value of date_diff()) has the number of days (actually stored in a property d, which could be used instead of getting a string with format()), which should always be a positive number. And for dates before today's date, the first date diff (i.e. $diffOne) will be 2+ days and the second date diff (i.e. $diffThreeDays) will be 4+ (see this playground example with debugging output above the table) so days before today's date will never be associated with either 'warning' or 'danger' according to the given logic.
A Solution
One approach (perhaps simpler) to comparing the dates is to use DateTime comparison
Note:
As of PHP 5.2.2, DateTime objects can be compared using comparison operators.1
So compare the DateTime variables (i.e. $due with $dueOneDay and $dueThreeDays) instead of the date differences.
In the logic below, you will notice that the order of the comparisons has been reversed, so the check for one day difference comes before the check for three days difference, in order to avoid the danger case never happening.
if ($due < $dueOneDay){
$highlight_css = " danger";
}else if ($due <= $dueThreeDays) {
$highlight_css = " warning";
}else {
$highlight_css = "";
}
With this approach, there is no need for the date_diff variables (i.e. $diffOne, $diffThree).
See a demonstration of this in this playground example.
1http://php.net/manual/en/datetime.diff.php#example-2524

PHP // Converting/Calculating DATE

Im trying to solve this since two days without success...
First of all my code:
PHP-Version: 5.4
SCRIPT1:
query = "SELECT start, end FROM timetable WHERE ........";
$result = mysql_query($query, $db) or die(mysql_error());
$sqlarr = mysql_fetch_array($result, MYSQL_ASSOC);
$times = array($sqlarr['start'], $sqlarr['end']);
$calced = strtotime($times[1]) - strtotime($times[0]);
$total = date("H:i:s", $calced-3600); //<-- -3600 Fixed it
echo "<br>Total: ".$total;
The start and end times are in format 00:00:00. Everytime this script calculates it appends 1 hour to the result. So if im going for a result of 5 minutes i´ll get 01:00:05.
Why???
This one is even more strange.
SCRIPT2:
while($row = mysql_fetch_array($result, MYSQL_ASSOC)){
echo " | ".$row['total']."<br>";
$add += strtotime($row['total']);}
echo $add;
The first script calculates the total time from start to end. The second one should get the total-times out of the db and calculate the sum of all entries. For every entry the second script substracts 2 hours.
Example:
Database => Start = 12:32:00 End = 12:32:15
Script1 Result1 = 01:00:15 (Where is this extra hour coming from?) FIXED
Every Result1 is stored in the same table(db). Script2 is loading all this rows and handling them by a while-loop.
According to how many entries there are the script subtracts serval hours.
0 Entries => Total: 00:00:00
1 Entry => Total: 23:00:xx
2 Entries => Total: 20:00:xx
3 Entries => Total: 18:00:xx
4 Entries => Total: 16:00:xx
So, with 2 entries it continues subtracting 2 hours from every total-calculation which isnt correct, abviously.
Thanks to you guys. Using DateTime made this simple and bugfree!
I am not really sure of your case with using strtotime function, but I've tried this solution and it worked, if you have php > 5.2:
You can use the DateTime class and date_diff function to get the difference date, read more here: http://us3.php.net/manual/en/datetime.diff.php
Example:
// start date
$start = new DateTime("09:23:38");
// end date
$end = new DateTime("09:23:54");
// calculate difference
$calc = date_diff($start, $end);
// prints 00:00:16
echo $calc->format('%h:%i:%s');
According to this answer It seems like by default date starts from 1:00:00 so you could subtract your date by 3600 seconds (1 hour) like this:
$start = strtotime("11:23:38");
$end = strtotime("11:23:54");
$calc = $end - $start;
echo date('H:i:s', $calc - 3600);
The problem comes from :
echo date('H:i:s', $calc);
$start - $end gives you the expected value in seconds. It depends on what value date('H:i:s', 0) is. On my computer, it's not midnight on 1 jan. 1970 !
This link as example : http://sandbox.onlinephpfunctions.com/code/81e56cbce16f6dacfedd2cb376b946a540bce36d.
You might consider substracting date('H:i:s', 0) from your result to get correct value.
Or using DateTime as #Ben Beri suggested you to do as it is (I think) the best way to do.

Counting number of working days and weekends using PHP/ MySQL

I am looking to calculate the number of weekdays worked and the number of weekend days worked from data i retrieve out of a MySQL database.
The time from the DB is formatted like this : 2013-07-01 07:00
This is what I have so far:
function isWeekend($date) {
$check = date("w", strtotime($date));
if ($check == 6 || $check == 0) {
return 1;
}
else {
return 0;
}
}
$query = mysql_query ("SELECT date from jobcards");
while ($row = mysql_fetch_assoc($query)) {
$date = $row['date'];
$date_check= isWeekend($date);
if ($date_check == 1) {
++$weekend;
}
else {
++$workday;
}
}
I need to find a way to count the days using mysql instead, is there such a way or a more elegant way to improve the PHP code ?
Also if I have multiple records in the database with the same date range but a different time example: 2013-07-01 07:00 and 2013-07-01 07:30 it will be counted as two workdays, how would i prevent that ?
Thanks.
Have a look at the WEEKDAY function, and compare it to 6/5 for Sunday/Saturday (respectively)
Your SQL will look something like:
SELECT SUM(IF(WEEKDAY(date) >= 5, 1, 0) AS WeekendCount,
SUM(IF(WEEKDAY(date) < 5, 1, 0) AS WeekdayCount
FROM jobcards
There is a similar answer here: MySQL Weekday/Weekend count - Part II
Fixed the ) of the IF being placed in the wrong place
SELECT SUM(IF(DAYOFWEEK(date) BETWEEN 2 AND 6,1,0) AS weekdays,
SUM(IF(DAYOFWEEK(date) NOT BETWEEN 2 AND 6,1,0) AS weekends,
FROM jobcards
SELECT date,
sum(date) total,
sum(if(date_format(date,'%w') between 1 and 5),1,0) weekday,
sum(date) - sum(if(date_format(date,'%w') between 1 and 5),1,0) weekend
from jobcards
If you want to do this in SQL, you can solve the timestamp problem using the MySQL DATE() function, and the DAYOFWEEK() function to count your weekdays/weekends (Note that the day numbers for Sat/Sun are 7/1 in MySQL and not 6/0 as in PHP). So to count the distinct weekday entries it would look something like:
SELECT COUNT(*) FROM jobcards WHERE DAYOFWEEK(DATE(jobcards.date)) BETWEEN 2 AND 6;

Ordering records that are timestamped

I have a tricky one here.
I have a number of results in a table, this table has id, market, date and points.
I want to get all the records for a given month and then output all the points for each day, in order.
I'm not sure of the best way to do this? Her are my options, well my ideas, I could set a loop that will execute for every day of the given month, this will run a query to find every record for that day and will do this for every day of the month, this will then be easy to order a format the records but i'm thinking its not a good idea to query the database that many times.
The second idea i to query the database for all the records for that date, order them by the date. However i can see come problems here when outputting the information for each day, well it will be a long job then the first i think.
Any ideas? anyone done something like this before and could lend a helping hand? Any advise would be more than welcome!
Thanks.
while($row = mysql_fetch_array($getresults))
{
$day = FALSE;
foreach ($row as $row)
{
$day_res = date("Y-m-d H:m:s", strtotime($row["date"]));
if (!$day || $day != $day_res)
{
$day = $day_res;
echo '<h1>'.$day.'</h1>';
}
echo $row['date'] . " " . $row["id"] . ": " . $row["market"] .
"/" . $row["points"] . "<br/>";
}
}
This is the output i get from it:
1970-01-01 00:01:00
3 3: 3/3
3 3: 3/3
2012-01-13 09:01:06
E E: E/E
E E: E/E
1970-01-01 00:01:00
2 2: 2/2
2 2: 2/2
- -: -/-
- -: -/-
1970-01-01 00:01:00
4 4: 4/4
4 4: 4/4
and that more or less repeats for a while.
Thanks again.
If that date is only a date, then you could try something like this:
SELECT t.date, SUM(t.points) FROM table t WHERE date BETWEEN [STARTDATE HERE] AND [ENDDATE HERE] GROUP BY t.date
What it does? It selects everything the date & the sum of the points of that day, because we group all the results based on a day. So for every unique date in your table you will get a row with a result. Use the WHERE date statement to define from which month you need the selection.
Addition: the thing shown is only valid if date is in format like: YYYY-MM-DD. In case you have a timestamp (YYYY-MM-DD HH:ii:ss) you could try to change the query to:
SELECT t.date, SUM(t.points) FROM table t WHERE date BETWEEN [STARTDATE HERE] AND [ENDDATE HERE] GROUP BY DAYOFMONTH(t.date)
This ensures that you will group them by the day of the month (1 to 31) based on the dates from your table.
at first the query to get all for a given month ordered by date:
SELECT * FROM `table` WHERE `date` LIKE '2012-01%' ORDER BY date;
here the php loop to show it:
$day = FALSE;
foreach ($results as $result){
$day_res = date("d", strtotime($result["date"]));
if (!$day || $day != $day_res){
$day = $day_res;
echo '<h1>'.$day.'</h1>';
}
echo $result["id"].": ".$result["market"]."/".$result["points"]."<br/>";
}
Ok here for your structure:
$day = FALSE;
while($row = mysql_fetch_array($getresults))
{
$day_res = date("Y-m-d H:m:s", strtotime($row["date"]));
if (!$day || $day != $day_res)
{
$day = $day_res;
echo '<h1>'.$day.'</h1>';
}
echo $row['date'] . " " . $row["id"] . ": " . $row["market"] .
"/" . $row["points"] . "<br/>";
}

Displaying Scheduled Events

I had this problem some years ago and back then I implemented a "different logic" in order to deliver the project but the doubt remains in my mind and hopefully with your help I'll be able to understand it now.
Suppose I have some scheduled events on my database that may or may not spawn over several days:
id event start end
-----------------------------------------------
1 fishing trip 2009-12-15 2009-12-15
2 fishCON 2009-12-18 2009-12-20
3 fishXMAS 2009-12-24 2009-12-25
Now I wish to display the events in a calendar, lets take the month of December:
for ($day = 1; $day <= 31; $day++)
{
if (dayHasEvents('2009-12-' . $day) === true)
{
// display the day number w/ a link
}
else
{
// display the day number
}
}
What query should the dayHasEvents() function do to check if there are (or not) events for the day? I'm guessing SELECT .. WHERE .. BETWEEN makes the most sense here but I've no idea how to implement it. Am I in the right direction?
Thanks in advance!
#James:
Lets say we're on December 19th:
SELECT *
FROM events
WHERE start >= '2009-12-19 00:00:00'
AND end <= '2009-12-19 23:59:59'
Should return the event #2, but returns nothing. =\
You should scratch that approach and grab all events for the given month up front so you only need to perform a single query as opposed to N queries where N is the number of days in the month.
You could then store the returned results in a multidimensional array like so:
// assume event results are in an array of objects in $result
$events = array();
foreach ($result as $r) {
// add event month and day as they key index
$key = (int) date('j', strtotime($r->start));
// store entire returned result in array referenced by key
$events[$key][] = $r;
}
Now you'll have a multidimensional array of events for the given month with the key being the day. You can easily check if any events exist on a given day by doing:
$day = 21;
if (!empty($events[$day])) {
// events found, iterate over all events
foreach ($events[$day] as $event) {
// output event result as an example
var_dump($event);
}
}
You're definitely on the right track. Here is how I would go about doing it:
SELECT *
FROM events
WHERE start <= '2009-12-01 00:00:00'
AND end >= '2009-12-01 23:59:59'
And you obviously just replace those date values with the day you're checking on.
James has the right idea on the SQL statement. You definitely don't want to run multiple MySQL SELECTs from within a for loop. If daysHasEvents runs a SELECT that's 31 separate SQL queries. Ouch! What a performance killer.
Instead, load the days of the month that have events into an array (using one SQL query) and then iterate through the days. Something like this:
$sql= "SELECT start, end FROM events WHERE start >= '2009-12-01' AND end <= '2009-12-31'";
$r= mysql_query($sql);
$dates= array();
while ($row = mysql_fetch_assoc($r)) {
// process the entry into a lookup
$start= date('Y-m-d', strtotime($row['start']));
if (!isset($dates[$start])) $dates[$start]= array();
$dates[$start][]= $row;
$end= date('Y-m-d', strtotime($row['end']));
if ($end != $start) {
if (!isset($dates[$end])) $dates[$end]= array();
$dates[$end][]= $row;
}
}
// Then step through the days of the month and check for entries for each day:
for ($day = 1; $day <= 31; $day++)
{
$d= sprintf('2009-12-%02d', $day);
if (isset($dates[$d])) {
// display the day number w/ a link
} else {
// display the day number
}
}
For your purposes a better SQL statement would be one that grabs the start date and the number of events on each day. This statement will only work properly if the start column is date column with no time component:
$sql= "SELECT start, end, COUNT(*) events_count FROM events WHERE start >= '2009-12-01' AND end <= '2009-12-31' GROUP BY start, end";

Categories