php query returns missing data - php

I have a problem with my php query for getting data. Let me explain what I want. I have a database which saves the active users for some hours in a day. For example 01-01-2012 13:00
active = 5 01-01-2012 14:00 active = 10. My php query should make an array which contains 2 columns which are date and active. But date must be like 01-01-2012 withour hours. So I grouped them as date2 but I couldn't find the active(sum) for each days. Here is my query which doesn't give the right active sums.
$query2 = mysql_query("SELECT DATE_FORMAT(date, '%Y-%m-%d') AS date2, SUM(active) FROM hit WHERE game= '".$game."' AND source = '".$source."' AND date > '".$dateFrom."' AND date < '".$dateTo."' GROUP BY date2 ORDER BY date2");
while($tuple= mysql_fetch_array($query2)){
$myArr[] = $tuple;
}
print_r($myArr);

You need to alias SUM(active) and GROUP BY date. If date is a datetime, you can GROUP BY DATE(date). Also, be careful with reserved words and make sure to use tick marks where necessary.
Further, if you are looking for an inclusive date range, you should know that when you send 2012-10-03, it is actually 2012-10-03 00:00:00 and any records between 00:00:00 and 23:59:59 will be ignored.
Lastly, you should stop using mysql_ functions as they are being deprecated.
This query should work:
$query2 = mysql_query("
SELECT DATE_FORMAT(`date`, '%Y-%m-%d') AS date2, SUM(active) AS active
FROM hit
WHERE game= '".$game."' AND source = '".$source."'
AND `date` > DATE_SUB('".$dateFrom."', INTERVAL 1 SECOND)
AND `date` < DATE_ADD('".$dateTo."', INTERVAL 1 DAY)
GROUP BY DATE(`date`)
ORDER BY `date`");
$i = 0;
while($tuple= mysql_fetch_array($query2)){
$myArr[$i][date2] = $tuple[date2];
$myArr[$i][active] = $tuple[active];
$i++;
}
print_r($myArr);
Your array ($myArr) would then look something like:
Array
(
[0] => Array
(
[date2] => 2012-10-03
[active] => 5
)
)

Related

Get last 12 months of data grouped by month even if 0

I am trying to get a COUNT of the last 12 months of appointments grouped by month for output into a chart. The following works fine but I need it to return 0 if no results for each month.
$query = "SELECT COUNT(id) as total_month FROM appointments WHERE created >= DATE(NOW()) - INTERVAL 365 DAY GROUP BY Month(created)";
$query = $mysqli->real_escape_string($query);
if($result = $mysqli->query($query)){
while($row = $result->fetch_array())
{
$month_total_appointments .= $row['total_month'].',';
}
}
echo $month_total_appointments;
================================================================
Simple table structure and example for appointments Table
id customer_name created
1 John 2020-05-01 08:00:00 <= stored as datetime
2 Mike 2020-04-01 09:00:00
3 Steve 2020-02-01 10:00:00
Output would be 0,0,0,0,0,0,0,0,1,0,1,1
======================================================
Current output is: 1,1,1
I've read some use a month table and LEFT JOIN but everything i've tried doesn't seem to work. Can anyone help please?
You won't get zeroes for rows that aren't there. Grouping combines rows that match particular criteria, but it can't fabricate them out of nothing.
That's why it's typical to include the grouping criteria in the results:
SELECT COUNT(id), MONTH(created) AS created_month
FROM appointments
WHERE created >= DATE(NOW()) - INTERVAL 365 DAY
GROUP BY created_month
Then you can expand that in your application code to fill in the missing values. The alternative is you need a fully populated list of all possible dates to JOIN against.
Keep in mind the MONTH() thing will wrap around and group January 2020 with January 2021. You may want to split this up:
SELECT COUNT(id), YEAR(created) AS created_year, MONTH(created) AS created_month
FROM appointments
WHERE created >= DATE(NOW()) - INTERVAL 365 DAY
GROUP BY created_year, created_month

Converting date inside loop breaks loop

So I have a MySQL query feeding into an array to sanitize it to meet the needs of a graphing library. I tried to convert the hours, currently in 24h format, to a slightly prettier 12h format. But when I do, it kills the loop after one iteration, and so only one value gets fed to the graph (instead of values for the whole day). I'm very new to PHP, so I'm not sure what is breaking this. What is the proper way to accomplish what I need to do?
$data = $conn->query('SELECT HOUR( TIMESTAMP ) AS HOUR , COUNT( DISTINCT detected_key ) AS num_rows
FROM Visitors
WHERE TIMESTAMP >= NOW( ) - INTERVAL 1 DAY
GROUP BY HOUR( TIMESTAMP )
ORDER BY `id` ASC
LIMIT 0 , 24');
while($row = $data->fetch(PDO::FETCH_ASSOC)) {
$prettytime = DATE("g:i a", STRTOTIME($row['HOUR']));
$visit[$prettytime]= $row['num_rows'];
}
EDIT: I tried using DATE_FORMAT already, but that still just outputs as the 24h format. The only documentation I found on the MySQL website always had it right after SELECT.
SELECT DATE_FORMAT( TIMESTAMP, "%l:%i %a" ) , HOUR( TIMESTAMP ) AS HOUR , COUNT( DISTINCT detected_key ) AS num_rows
FROM Visitors
WHERE TIMESTAMP >= NOW( ) - INTERVAL 1 DAY
GROUP BY HOUR( TIMESTAMP )
ORDER BY `id` ASC
LIMIT 0 , 24
Here is the website where you can see the graph. The one using the query above is the top left graph.
This would seem to work rather nicely from a database field defined as
`thetime` timestamp NULL DEFAULT CURRENT_TIMESTAMP
Then using the MySQL DATE_FORMAT() and HOUR() functions :-
SELECT thetime, DATE_FORMAT(thetime, '%r') from test_table
Gives the results :-
2014-01-20 10:57:45, 10:57:45 AM
2014-01-20 23:59:31, 11:59:31 PM
Or
SELECT thetime, HOUR(DATE_FORMAT(thetime, '%r')) from test_table
Gives :-
2014-01-20 10:57:45, 10
2014-01-20 23:59:31, 11

get the nearest date mysql

I have the following dates in my table. How do I find a closest date from either today (if today's date is there) or if today's date is not there then the nearest past date?
2012-10-01 aa123
2012-10-02 aa43
2012-10-03 aa478
2012-10-04 aa40
2012-10-05 aa54
2012-10-06 de34
2012-10-07 a5434
2012-10-08 r4t
2012-10-09 x34
2012-10-10 q23
2012-10-11 b53
So if today is '2012-10-07' is then the record will be a5434. But if 2012-10-07 is missing then the record will be de34 which belongs to 2012-10-06 since that would be the closest past day from today.
I am not sure where to start on this one, so I haven't tried anything yet. Need a sql solution to this.
It's simple, just get one of the last date <= the current date:
$now = date("Y-m-d");
$sql = "SELECT * FROM date_table where date_field <= '$now' ORDER BY date_field DESC LIMIT 1 OFFSET 1";
Add an ORDER BY statement to the query. The following will order the rows by their date, with the latest at the top and oldest at the bottom.
SELECT `id`, `date` FROM `table` ORDER BY `date` DESC LIMIT 1;

How to minimize the load in queries that need grouping with different invervals?

I'm looking for a best practice advice how to speed up queries and at the same time to minimize the overhead needed to invoke date/mktime functions. To trivialize the problem I'm dealing with the following table layout:
CREATE TABLE my_table(
id INTEGER PRIMARY KEY NOT NULL AUTO_INCREMENT,
important_data INTEGER,
date INTEGER);
The user can choose to show 1) all entries between two dates:
SELECT * FROM my_table
WHERE date >= ? AND date <= ?
ORDER BY date DESC;
Output:
10-21-2009 12:12:12, 10002
10-21-2009 14:12:12, 15002
10-22-2009 14:05:01, 20030
10-23-2009 15:23:35, 300
....
I don't think there is much to improve in this case.
2) Summarize/group the output by day, week, month, year:
SELECT COUNT(*) AS count, SUM(important_data) AS important_data
FROM my_table
WHERE date >= ? AND date <= ?
ORDER BY date DESC;
Example output by month:
10-2009, 100002
11-2009, 200030
12-2009, 3000
01-2010, 0 /* <- very important to show empty dates, with no entries in the table! */
....
To accomplish option 2) I'm currently running a very costly for-loop with mktime/date like the following:
for(...){ /* example for group by day */
$span_from = (int)mktime(0, 0, 0, date("m", $time_min), date("d", $time_min)+$i, date("Y", $time_min));
$span_to = (int)mktime(0, 0, 0, date("m", $time_min), date("d", $time_min)+$i+1, date("Y", $time_min));
$query = "..";
$output = date("m-d-y", ..);
}
What are my ideas so far? Add additional/ redundant columns (INTEGER) for day (20091212), month (200912), week (200942) and year (2009). This way I can get rid of all the unnecessary queries in the for loop. However I'm still facing the problem to very fastly calculate all dates that doesn't have any equivalent in database. One way to simply move the problem could be to let MySQL do the job and simply use one big query (calculate all the dates/use MySQL date functions) with a left join (the data). Would it be wise to let MySQL take the extra load? Anyway I'm reluctant to use all these mktime/date in the for loop. Since I have complete control over the table layout and code even suggestions with major changes are welcome!
Update
Thanks to Greg I came up with the following SQL query. However it still bugs me to use 50 lines of sql statements - build up with php - that maybe could be done faster and more elegantly otherwise:
SELECT * FROM (
SELECT DATE_ADD('2009-01-30', INTERVAL 0 DAY) AS day UNION ALL
SELECT DATE_ADD('2009-01-30', INTERVAL 1 DAY) AS day UNION ALL
SELECT DATE_ADD('2009-01-30', INTERVAL 2 DAY) AS day UNION ALL
SELECT DATE_ADD('2009-01-30', INTERVAL 3 DAY) AS day UNION ALL
......
SELECT DATE_ADD('2009-01-30', INTERVAL 50 DAY) AS day ) AS dates
LEFT JOIN (
SELECT DATE_FORMAT(date, '%Y-%m-%d') AS date, SUM(data) AS data
FROM test
GROUP BY date
) AS results
ON DATE_FORMAT(dates.day, '%Y-%m-%d') = results.date;
You definitely shouldn't be doing a query inside a loop.
You can group like this:
SELECT COUNT(*) AS count, SUM(important_data) AS important_data, DATE_FORMAT('%Y-%m', date) AS month
FROM my_table
WHERE date BETWEEN ? AND ? -- This should be the min and max of the whole range
GROUP BY DATE_FORMAT('%Y-%m', date)
ORDER BY date DESC;
Then pull these into an array keyed by date and loop over your data range as you are doing (that loop should be pretty light on CPU).
Another idea is not to use string inside the query. Transform the string parameter to datetime, on mysql.
STR_TO_DATE(str,format)
http://dev.mysql.com/doc/refman/5.0/en/date-and-time-functions.html

Group By and displaying entries under date

Here's my data:
id date
1 2009-01-01 10:15:23
2 2009-01-01 13:21:29
3 2009-01-02 01:03:13
4 2009-01-03 12:20:19
5 2009-01-03 13:01:06
What I'd like to do is group by each date and then list the id numbers below each date. Should I be getting the distinct date values and then cycling through them to get the entries that fall on the date? is this efficient?
so my output would look like:
2009-01-01
1
2
2009-01-02
3
2009-01-03
4
5
$query = "SELECT DATE(date) as mydate, id FROM your_data_table ORDER BY mydate, id";
$result = $pdo->query($query);
$oldDate = false;
while ( list($date, $id) = $result->fetch(PDO::FETCH_NUM) ) {
if ( $oldDate != $date ) {
echo "$date\n$id\n";
$oldDate = $date;
} else {
echo "$id\n";
}
}
Instead of doing it in several queries, you just fetch all the data at once. If you only need some dates then you just put a WHERE clause before the ORDER BY.
Your stored dates have time information (the type of the column is DATETIME or TIMESTAMP), in order to do a group by, you need to extract only the date part your timespan, you can use the DATE() function.

Categories