Related
I have two tables to be joined, 1 is user and 1 is attendance.
TABLE : attendance
id userId totalHours
1 1 0745
2 3 0845
3 1 0945
TABLE : user
id name departmentId
1 John 2
2 Sean 2
3 Allan 2
Not every user have attendance record (their totalHours)
But I need to query by userId WHERE departmentId = XXXX and SUM each of their totalHours that exist, without neglecting the userId without any record in attendance.
So far I made this:
$result = mysqli_query($con,"SELECT * FROM user WHERE departmentId = 2");
while($row = mysqli_fetch_array($result))
{
$id = $row['userId'];
$result2 = mysqli_query($con,"SELECT * FROM attendance WHERE userId = $id");
while($row2 = mysqli_fetch_array($result2))
$totalHours = 0;
{
$totalHours = $row2['totalHours'];
$grandTotal += $totalHours;
$totalHoursInHHmm = substr_replace($totalHours,":",2,0);
$parsed = date_parse($totalHoursInHHmm);
$toSeconds = $parsed['hour'] * 3600 + $parsed['minute'] * 60;
$total += $toSeconds;
$init = $total;
$hours = floor($init / 3600);
$minutes = floor(($init / 60) % 60);
}
echo "$hours:$minutes";
}
The result shows all the user in the department, and did SUM all the totalHours for each userId , but what was wrong is, there are userId without any attendance still have the SUM value shown, inheriting previous total Sum
Any help is appreciated :)
I need to query by userId WHERE departmentId = XXXX and SUM each of
their totalHours that exist, without neglecting the userId without any
record in attendance.
To show the hours for all users in a given department, even users w/o rows in the attendance table, use a LEFT JOIN
Use (CAST(totalHours AS UNSIGNED) % 100)/60 + FLOOR(CAST(totalHours AS UNSIGNED)/100) to convert your varchar hours+minutes to a single number of hours.
$query = "SELECT u.id,
SUM((CAST(totalHours AS UNSIGNED) % 100)/60 + FLOOR(CAST(totalHours AS UNSIGNED)/100)) grandTotal
FROM user u
LEFT JOIN attendance a
ON u.id = a.userId
WHERE u.departmentId = 2
GROUP BY u.id";
$result = mysqli_query($con,$query);
while($row = mysqli_fetch_array($result)) {
print $row['id'] . ' ' . $row['grandTotal'];
}
try this, just in the first while you wont need both.
SELECT TIME_FORMAT(sum(STR_TO_DATE(a.totalHours, '%i')),'%H:%i') as sum, u.id, u.name FROM user AS u LEFT JOIN attendance AS a ON a.userId = u.id WHERE u.departmentId = 2 AND u.id = $user_id GROUP by u.id;
Update, try that not sure if it will work I cant test it right now but refer to this question.
how to convert weird varchar "time" to real time in mysql?
Once you get the right query working it will be really easy in php to do the rest. The DB should do this work, although the schema is not ideal here..
OK! It's happening because, the users that doesn't have any attendance isn't passing through the second while, then the values aren't being restarted. You can correct this simply setting $grandTotal after you echo it. Like this:
$result = mysqli_query($con,"SELECT * FROM user WHERE departmentId = 2");
while($row = mysqli_fetch_array($result))
{
$id = $row['userId'];
$result2 = mysqli_query($con,"SELECT * FROM attendance WHERE userId = $id");
while($row2 = mysqli_fetch_array($result2))
{
$totalHours = 0;
$totalHours = $row2['totalHours'];
$grandTotal += $totalHours
}
echo $grandTotal;
$grandTotal = 0;
}
What I understood from the question is NOT to neglect those userid even if they do not have their attandance record. In this scenario I have 2 Options to be chosen ...
1.
$result = mysqli_query($con,"SELECT * FROM user WHERE departmentId = 2");
while($row = mysqli_fetch_array($result))
{
$id = $row['userId'];
$result2 = mysqli_query($con,"SELECT * FROM attendance WHERE userId = $id");
$grandTotal=0;
while($row2 = mysqli_fetch_array($result2))
$totalHours = 0;
{
$totalHours = $row2['totalHours'];
$grandTotal += $totalHours
}
echo $grandTotal;
}
2.
$result = mysqli_query($con,"SELECT * FROM user WHERE departmentId = 2");
while($row = mysqli_fetch_array($result))
{
$id = $row['userId'];
$result2 = mysqli_query($con,"SELECT * FROM attendance WHERE userId = $id");
while($row2 = mysqli_fetch_array($result2))
$totalHours = 0;
{
$totalHours = $row2['totalHours'];
if($totalHours<=0)
$grandTotal=0;
$grandTotal += $totalHours
}
echo $grandTotal;
}
Move $totalhours = 0 within the curly braces {}.
Set $hours = $minutes = 0 at the top of the second while loop (where you set $totalhours = 0)
**If you don't reset $hours and $minutes, users who don't have attendance will get the old values.
I would like to calculate the total distance of driving beetween multiple locations (loop), including the distance (starting point (garage) - first location sarting point) and (last location finishig point - finishing point (garage)).
Example:
(Garage + D1) + (D1 + D2) + (D2 + E1) + (E1 + E2) + E2 + Garage)
I'm having a problem with the correct looping. Here's my simplified code:
<?
$driver = 5;
$result2 = mysql_query("SELECT * FROM test WHERE id='$driver' LIMIT 1") or die(mysql_error());
while($row2 = mysql_fetch_array( $result2 )) {
$lon=$row2['lon'];
$lat=$row2['lat'];
echo "$lon, $lat";
}
$result = mysql_query("SELECT * FROM test1 WHERE driver='$driver'") or die(mysql_error());
while($row = mysql_fetch_array( $result )) {
$lon1=$row['lon1'];
$lat1=$row['lat1'];
$lon2=$row['lon2'];
$lat2=$row['lat2'];
////////// distance between driver address and starting address
$distancecalc = (3958*3.1415926*sqrt(($lat-$lat1)*($lat-$lat1) + cos($lat/57.29578)*cos($lat1/57.29578)*($lon-$lon1)*($lon-$lon1))/180);
////////// distance between statring address and finishing address - multiple adsresses
$distancecalc1 = $distancecalc1 + (3958*3.1415926*sqrt(($lat2-$lat1)*($lat2-$lat1) + cos($lat2/57.29578)*cos($lat1/57.29578)*($lon2-$lon1)*($lon2-$lon1))/180);
////////// distance between finishing address and driver address
$distancecalc2 = (3958*3.1415926*sqrt(($lat2-$lat)*($lat2-$lat) + cos($lat2/57.29578)*cos($lat/57.29578)*($lon2-$lon)*($lon2-$lon))/180);
$distancetotal = $distancecalc + $distancecalc1 +$distancecalc2;
echo "$distancecalc<br>
$distancecalc1<br>
$distancecalc2<br>";
}
echo "$distancetotal";
?>
I tried some things (mostly if... ) and also with more database requests, but I'm still having the problem of avoiding multiple calculations, and I also strongly believe there is way to code it to make it easier and clearer.
I would appreciate some help on this one.
Thank you very much.
Here's the function you need:
function distance ($lat1, $lon1, $lat2, $lon2) {
return (3958*3.1415926*sqrt(($lat2-$lat1)*($lat2-$lat1) + cos($lat2/57.29578)*cos($lat1/57.29578)*($lon2-$lon1)*($lon2-$lon1))/180);
}
The function body is exactly the same as the formula you used in-line, so I don't understand why you needed help with this.
The number 6367 - is the radius of the Earth in kilometers
DELIMITER $$
DROP FUNCTION IF EXISTS geodist $$
CREATE FUNCTION geodist (
src_lat DECIMAL(9,6), src_lon DECIMAL(9,6),
dst_lat DECIMAL(9,6), dst_lon DECIMAL(9,6)
) RETURNS DECIMAL(6,2) DETERMINISTIC
BEGIN
SET #dist := 6367 * 2 * ASIN(SQRT(
POWER(SIN((src_lat - ABS(dst_lat)) * PI()/180 / 2), 2) +
COS(src_lat * PI()/180) *
COS(ABS(dst_lat) * PI()/180) *
POWER(SIN((src_lon - dst_lon) * PI()/180 / 2), 2)
));
RETURN #dist;
END $$
DELIMITER ;
Ok. I worked it out with help of mac_gyver (php freaks). All calculations are done as i wish. My code below:
<?
include "connectdb.php";
$driver = 5;
$datestamp = '2013/05/07';
$result2 = mysql_query("SELECT * FROM drivers WHERE id='$driver' LIMIT 1") or die(mysql_error());
while($row2 = mysql_fetch_array( $result2 )) {
$lon=$row2['lon'];
$lat=$row2['lat'];
}
$result = mysql_query("SELECT * FROM quotedb WHERE moveday='$datestamp' AND driver='$driver' AND cleared='Not Cleared' AND status='Done' ORDER BY moveday, timeday") or die(mysql_error());
$distance = 0; // accumulate the distance
$first_pass = true; // flag to detect the first row inside the loop
while($row = mysql_fetch_assoc( $result )) {
$lon2a=$lon2;
$lat2a=$lat2;
$lon1=$row['lon1'];
$lat1=$row['lat1'];
$lon2=$row['lon2'];
$lat2=$row['lat2'];
// calculate the distance from the Garage to the first point of the first row
if($first_pass){
$distance += (3958*3.1415926*sqrt(($lat-$lat1)*($lat-$lat1) + cos($lat/57.29578)*cos($lat1/57.29578)*($lon-$lon1)*($lon-$lon1))/180);
$first_pass = false;
}
// calculate the distance for each row (segment) in the route
$distance += (3958*3.1415926*sqrt(($lat2-$lat1)*($lat2-$lat1) + cos($lat2/57.29578)*cos($lat1/57.29578)*($lon2-$lon1)*($lon2-$lon1))/180);
if ( $lon2a == "" or $lat2a =="" ) {
} else {
// calculate the distance from the second point of the first row to the first point of the next row
$distance += (3958*3.1415926*sqrt(($lat2a-$lat1)*($lat2a-$lat1) + cos($lat2a/57.29578)*cos($lat1/57.29578)*($lon2a-$lon1)*($lon2a-$lon1))/180);
}
}
// calculate the distance from the second point of the last row to the Garage
$distance += (3958*3.1415926*sqrt(($lat2-$lat)*($lat2-$lat) + cos($lat2/57.29578)*cos($lat/57.29578)*($lon2-$lon)*($lon2-$lon))/180);
echo "$distance<br>
";
?>
Still think there is a place for improvement in the code. Will aplay Haversine method for calculations. Do you guys have some suggestion to improve this piece of code ... thx
Impoved version:
<?
include "connectdb.php";
$driver = 5;
$datestamp = '2013/05/07';
$result2 = mysql_query("SELECT * FROM drivers WHERE id='$driver' LIMIT 1") or die(mysql_error());
while($row2 = mysql_fetch_array( $result2 )) {
$garage_lon=$row2['lon'];
$garage_lat=$row2['lat'];
}
$result = mysql_query("SELECT * FROM quotedb WHERE moveday='$datestamp' AND driver='$driver' AND cleared='Not Cleared' AND status='Done' ORDER BY moveday, timeday") or die(mysql_error());
function calculate_distance($lon1, $lat1, $lon2, $lat2) {
return (3958 * 3.1415926 * sqrt(($lat2 - $lat1) * ($lat2 - $lat1) + cos($lat2 / 57.29578) * cos($lat1 / 57.29578) * ($lon2 - $lon1) * ($lon2 - $lon1)) / 180);}
$previous_lon = $garage_lon;
$previous_lat = $garage_lat;
$distance = 0; // accumulate the distance
while($row = mysql_fetch_assoc( $result ))
{
$lon1 = $row['lon1'];
$lat1 = $row['lat1'];
$lon2 = $row['lon2'];
$lat2 = $row['lat2'];
if ( $previous_lon && $previous_lat )
{
// calculate the distance from the second point of the first row to the first point of the next row
$distance += calculate_distance($lon1, $lat1, $previous_lon, $previous_lat);
}
// calculate the distance for each row (segment) in the route
$distance += calculate_distance($lon1, $lat1, $lon2, $lat2);
$previous_lon = $lon2;
$previous_lat = $lat2;
}
// calculate the distance from the second point of the last row to the Garage
$distance += calculate_distance($garage_lon, $garage_lat, $lon2, $lat2);
$distance = round($distance,0);
echo "$distance<br>
";
?>
I am calculating one parameter(X) this way:
<?php
$link=mysql_connect("localhost","root","");
mysql_select_db("hand");
$result = mysql_query("select * from sessions",$link);
$num_calls = mysql_num_rows($result);
echo '<br/>'."\n Total X :\n $num_calls ".'<br/>';
?>
I am calculating another parameter(Y) this way:
<?php
mysql_connect("localhost","root","");
mysql_select_db("hand");
$result = mysql_query("SELECT * FROM cars WHERE start_at > TIMESTAMPADD(WEEK, -4, CURDATE()) AND TIMESTAMPDIFF(MINUTE, start_at, end_at) > 5");
$num_rows = mysql_num_rows($result);
echo '<br/>'."\n Total Y :\n $num_rows ".'<br/>';
?>
At another place I need the to calculate the ration X/Y. How do I do that as it need to be done in another field in the same html page.
You do:
$num_calls = intval($num_calls); // Make sure these are integers
$num_rows = intval($num_rows);
$calculation = $num_calls / $num_rows;
echo $calculation;
Hmmm
First that's a very expensive way of getting a count.
Select Count(*) from Sessions
and
Select Count(*) from Cars...
So something like
Select Cast(Select Count(*) From Sessions) as FLOAT) / (Select Count(*) From Cars...)
should get you close.
Then you don't need to bother persisting it, though if you insist, all the usual mechanisms apply.
you could put the query strings into an array like this:
$queryArray = array("select * from sessions", "SELECT * FROM cars WHERE start_at >
TIMESTAMPADD(WEEK, -4, CURDATE()) AND TIMESTAMPDIFF(MINUTE, start_at, end_at) > 5");
use a foreach just once:
foreach($query in $queryArray)
{
$result = mysql_query($query);
$count = mysql_count($result);
$countList[] = $count
}
$number = 0;
and now you need to make the ratio out of the items from count with another foreach
foreach($number in $countList)
{<br/>
$number = $number + $number;
}<br/>
$ratio = $number / count($countList);
Like this you can add all your future queries as strings and everything will still work.
P.S. I am unable to test the code at the moment but it should be quite functional. Not really sure about the foreach as.
Ok I think I understand.
You want X:Y where X is the ratio to Y:
$link=mysql_connect("localhost","root","");
mysql_select_db("hand");
$result = mysql_query("select * from sessions",$link);
$num_calls_x = mysql_num_rows($result);
$result = mysql_query("SELECT * FROM cars WHERE start_at > TIMESTAMPADD(WEEK, -4, CURDATE()) AND TIMESTAMPDIFF(MINUTE, start_at, end_at) > 5");
$num_calls_y = mysql_num_rows($result);
echo '<br/>'."\n Total X :\n $num_calls_x ".'<br/>';
echo '<br/>'."\n Total Y :\n $num_calls_y ".'<br/>';
echo 'Ratio: '.round(($num_calls_x/ $num_calls_y) * 100, 0);
You can put this values to session/cookie and read it in another place.
I have two columns in a row: min_value, max_value. Is there a way to do a select like:
SELECT RAND(`min_v`, `max_v`) `foo` [..]
I do realize that RAND does a different thing; the closest I came up (with help) is (RAND() * (max-min))+min, though it will produce a float number, which I'd need then to ROUND() and this is just completely wrong.
Unless anyone can suggest an alternative (which would be very useful), I will go PHP way.
Actually, ROUND((RAND() * (max-min))+min) is the best way in MySQL to do what you'd like. It is also the best way in ActionScript, JavaScript, and Python. Honestly, I prefer it to the PHP way because it is more convenient.
Because I don't know how many rows you'll be returning, I can't advise you whether it is better to use PHP or MySQL for this, but if you're dealing with a large number of values you probably are better off using MySQL.
Addendum
So, there was a question as to whether this is better in PHP or MySQL. Instead of getting into a debate on principles, I ran the following:
<pre><?php
$c = mysql_connect('localhost', 'root', '');
if(!$c) die('!');
echo mysql_select_db('test', $c)?'Connection':'Failure';
echo PHP_EOL;
echo ':::::::::::::::::::::::::BEGINNING MYSQL RAND::::::::::::::::::::::::::::::'.PHP_EOL;
$start = microtime(1);
for( $i = 0; $i < 100000; $i++ )
{
$r = mysql_query( 'SELECT ROUND(RAND() * (200-10) + 10) FROM dual' );
$r = mysql_fetch_array( $r );
}
$end = microtime(1);
echo ($end - $start) . " for MySQL select".PHP_EOL;
echo ':::::::::::::::::::::::::BEGINNING PHP RAND::::::::::::::::::::::::::::::' .PHP_EOL;
$start = microtime(1);
for( $i = 0; $i < 100000; $i++ )
{
$r = mysql_query( 'SELECT 200 AS two, 10 AS tem FROM dual' );
$r = mysql_fetch_array( $r );
$r[2]= rand($r[0], $r[1]);
}
$end = microtime(1);
echo ($end - $start) . " for PHP select".PHP_EOL;
MySQL is faster by about 2-3%.
If you use this, however (note, more columns return by MySQL):
<pre><?php
$c = mysql_connect('localhost', 'root', '');
if(!$c) die('!');
echo mysql_select_db('test', $c)?'Connection':'Failure';
echo PHP_EOL;
echo ':::::::::::::::::::::::::BEGINNING MYSQL RAND::::::::::::::::::::::::::::::'.PHP_EOL;
$start = microtime(1);
for( $i = 0; $i < 100000; $i++ )
{
$r = mysql_query( 'SELECT ROUND(RAND() * (200-10) + 10) as rd, 200 as two, 10 as ten FROM dual' );
$r = mysql_fetch_array( $r );
}
$end = microtime(1);
echo ($end - $start) . " for MySQL select".PHP_EOL;
echo ':::::::::::::::::::::::::BEGINNING PHP RAND::::::::::::::::::::::::::::::' .PHP_EOL;
$start = microtime(1);
for( $i = 0; $i < 100000; $i++ )
{
$r = mysql_query( 'SELECT 200 AS two, 10 AS tem FROM dual' );
$r = mysql_fetch_array( $r );
$r[2]= rand($r[0], $r[1]);
}
$end = microtime(1);
echo ($end - $start) . " for PHP select".PHP_EOL;
MySQL comes out behind by 3-4% (very inconsistent results) (about the same results if you don't use an array index assignment for $r[2]).
The major difference, it seems, comes from the number of records return to PHP and not the randomization system itself. So, if you need column A, column B, and a random value, use PHP. If you only need the random value, then use MySQL.
This method guarantees the same statistical probability for each value:
SELECT FLOOR((RAND() * (max-min+1))+min)
in case minimum range is 1, you can simply
SELECT FLOOR((RAND() * max_range) + 1)
in case minimum range is 0, you can even more simply
SELECT FLOOR((RAND() * max_range))
Could you do something like this?
SELECT id, (FLOOR( 1 + RAND( ) *60 )) AS timer
FROM users
LIMIT 0 , 30
See this post
Depending on how many rows you have in the table(s), using rand() in a query or subquery can be extremely slow.
You can seriously improve the speed by first putting the random value in a variable and then just using that in your query.
For example on a table with over 4 million rows...
This took over 10 minutes:
SELECT
*
FROM
`customers` `Customer`
WHERE
`id` = (
SELECT
FLOOR((RAND() * (max(`CustomerRand`.`id`) - min(`CustomerRand`.`id`) + 1)) + min(`CustomerRand`.`id`)) `random_id`
FROM
`customers` `CustomerRand`
);
While this took about 3 seconds on average:
SELECT
FLOOR((RAND() * (max(`CustomerRand`.`id`) - min(`CustomerRand`.`id`) + 1)) + min(`CustomerRand`.`id`)) `random_id`
FROM `customers` `CustomerRand` INTO #rand_id;
SELECT * FROM `customers` WHERE `id` = #rand_id;
You might even be able to put this into a stored procedure then if it's something you would want to do or re-use often. The stored procedure could then be called from PHP or Python or wherever
You can use order by rand
SELECT virtual.num
FROM (
SELECT 1 AS num UNION
SELECT 2 AS num UNION
SELECT 3 AS num
) virtual
ORDER BY RAND()
LIMIT 1
Hi Im making a website in php with a microsoft access database through odbc and i have come across a small problem let me show you my codes first.
<form method ="POST" action="maxtimestamplog.php">
Longitude <input type="text" name="longitude" /><br/>
Latitude
<input type ="text" name = "latitude"/>
<input name="ok" type="submit" value="Submit" />
</form>
<?
$dbc = odbc_connect("X1","",""); // Trying to establish connection
// with database
echo "<br>";
$datetime =date('Y-m-d H:i:s', strtotime('+8 hours'));
echo "The Current date/time is $datetime";
echo "<br><br><br>";
if (!$dbc)
{
exit("Connection failed:".$dbc);
}
$x_origin = $_POST['longitude'];
$y_origin = $_POST['latitude'];
$query = " SELECT m.vehicle_no,
l.longitude,
l.latitude,
l.timestamp
FROM
vehicle_log AS l,
GPS_modem AS m
WHERE
m.modem_ID = l.modem_ID
AND l.timestamp = (
SELECT MAX(timestamp) FROM vehicle_log WHERE modem_ID = l.modem_ID
) order by timestamp desc
"; // SQL Statement
$rs = odbc_exec($dbc,$query);
if (!$rs)
{
exit("Error in SQL. ".$rs);
}
$i=1;
while (odbc_fetch_row($rs))
{
$lng = odbc_result($rs, "longitude");
$lat = odbc_result ($rs, "latitude");
$ts = odbc_result ($rs, "timestamp");
$vno = odbc_result ($rs, "vehicle_no");
$yyyy= substr($ts, 0, 4);
$mm= substr($ts, 5, 2);
$dd= substr($ts, 8,2);
$hr= substr($ts, 11, 2);
$min= substr($ts, 14,2);
$sec= substr($ts,17, 2);
$cyyyy= substr($datetime, 0, 4);
$cmm= substr($datetime, 5, 2);
$cdd= substr($datetime, 8,2);
$chr= substr($datetime, 11, 2);
$cmin= substr($datetime, 14,2);
$csec= substr($datetime,17, 2);
$ctss = $csec
+ ($cmin * 60)
+ ($chr * 60 * 60)
+ ($cdd * 24 * 60 * 60)
+ ($cmm * 30 * 24 * 60 * 60)
+ ($cyyyy * 365 * 24 * 60 * 60);
$tss = $sec
+ (cmin * 60) + ($hr * 60 * 60)
+ ($dd * 24 * 60 * 60)
+ ($mm * 30 * 24 * 60 * 60)
+ ($yyyy * 365 * 24 * 60 * 60);
$tssd = $ctss - $tss;
$x = $lng;
$y = $lat;
$i = $i + 1;
$xd = ($x - $x_origin);
$yd = ($y - $y_origin);
$d = ($xd*$xd) + ($yd*$yd);
$td = sqrt($d);
echo "Car Number $vno is at a distance $td away at timestamp $ts";
echo "</br></br>";
}
odbc_close($dbc);
?>
</HTML>
what I want to be done now is to only display one output not all.
Firstly I want to select only the data where variable $tssd is less than or equal to 10800 then i want to display the smallest $td out of what i selected and it shoud display
echo "Car Number $vno is at a distance $td away at timestamp $ts";
echo "</br></br>";
where $td is the least within $tssd is less than 10800
It should only display one
please try to help me im quite new to php and my sql statement is quite messy as it is i make blunder out of it.
Try to separate the SQL from the PHP.
I believe you're asking how to return only a single row rather than all the rows. To accomplish this, use the "TOP" keyword. For example,
SELECT TOP 1
m.vehicle_no,
l.longitude,
l.latitude,
l.timestamp
For the second part of your question, "where $td is the least within $tssd is less than 10800", we do not know the column names that these relate to. If we pretend they're named TD and TSSD in the vehicle_log table, then your statement might look like this:
SELECT TOP 1 m.vehicle_no,
l.longitude,
l.latitude,
l.timestamp,
l.TD,
l.TSSD
FROM
vehicle_log AS l,
GPS_modem AS m
WHERE
m.modem_ID = l.modem_ID
AND l.timestamp = (
SELECT MAX(timestamp) FROM vehicle_log WHERE modem_ID = l.modem_ID
)
AND l.TD <= 10800
order by l.TD ASC
Lastly, I changed the ORDER BY to accommodate "i want to display the smallest $td".
If this is not what you are looking for, can you provide more information?