MySQL + PHP (grouping by date) - php

I'm trying to output a list of different itmes grouped by the date they were stored in the database (unix timestamp).
I'd need help with both MySQL (query) and PHP (output).
MySQL table
id | subject | time
1 | test1 | 1280278800
2 | test2 | 1280278800
3 | test3 | 1280365200
4 | test4 | 1280451600
5 | test5 | 1280451600
OUTPUT
Today
test5
test4
Yesterday
test3
July 28
test2
test1
I'd appreciate any help on this. Thanks!;-)

You can convert your unix timestamp to a date using DATE(FROM_UNIXTIME(time)). This will output something like 2010-07-30.
The following should group by the date.
SELECT id, subject, time, DATE(FROM_UNIXTIME(time)) AS date_column
GROUP BY date_column
Edit: Didn't read your question correctly.
For this I would just run a standard SELECT and ORDER BY time DESC.
Then do the grouping with PHP.
$lastDate = null;
foreach ($rows as $row) {
$date = date('Y-m-d', $row['time']);
$time = date('H:i', $row['time']);
if (is_null($lastDate) || $lastDate !== $date) {
echo "<h2>{$date}</h2>";
}
echo "{$time}<br />";
$lastDate = $date;
}

you could create a mysql udf for that, something like this:
DELIMITER $$
DROP FUNCTION IF EXISTS `niceDate` $$
CREATE FUNCTION `niceDate` (ts INT) RETURNS VARCHAR(255) NO SQL
BEGIN
declare dt DATETIME;
declare ret VARCHAR(255);
set dt = FROM_UNIXTIME(ts);
IF DATE_FORMAT(dt, "%Y%m%d") = DATE_FORMAT(NOW(), "%Y%m%d") THEN SET ret = 'Today';
ELSEIF DATE_FORMAT(dt, "%Y%m%d") = DATE_FORMAT(DATE_ADD(NOW(), INTERVAL -1 DAY), "%Y%m%d") THEN SET ret = 'Yesterday';
ELSE SET ret = CONCAT(DATE_FORMAT(dt, "%M "), DATE_FORMAT(dt, "%d"));
END IF;
RETURN ret;
END $$
DELIMITER ;
You could then construct your query like this:
select niceDate(your_date_field) from table group by niceDate(your_date_field) order by your_date_field desc
disclaimer: i haven't tested this function, but you should get the idea.

The easiest way is probably to sort by timestamp (descending) in SQL, and do the grouping in PHP: Iterate over the result set, converting the timestamp to a DateTime object; when the current date is on a different day than the previous one (hint: use DateTime::format()), insert a sub-heading.

Related

How to determine if a giving start and end time has expire in php

Please i want to create a php code to check if time has expired or still active based on the start date and end time that users specified.
Below is the database structure and sample php code which i tried.
game_config
game_id | start_date | start_t | start_h | end_time | end_h | status
--------|------------|---------|---------|----------|-------|---------
100 | 03/26/2018 | 10:45 | PM | 12:30 | AM | 0
101 | 03/27/2018 | 09:23 | AM | 11:10 | AM | 0
Php code sample
<?php
$conf_execution_date = date('m/d/Y'); /*Current date*/
$conf_execution_meridiem = date('A'); /*Current meridiem*/
$conf_execution_timer = date('h:m'); /*Current time*/
$conf_execution_proccess = false; /*Proceed or not*/
$conf_handler = $appconn->prepare("
SELECT * FROM game_config WHERE game_id = :game_id AND start_date = :start_dateAND start_t = :start_t AND status = 0 LIMIT 1
");
$conf_handler->bindParam(":game_id", 100);
$conf_handler->bindParam(":start_date", $conf_execution_date);
$conf_handler->bindParam(":start_t", $conf_execution_meridiem);
$conf_handler->execute();
$appconf = $conf_handler->fetch(PDO::FETCH_OBJ);
if($appconf){
$config_start_time = strtotime($appconf->start_t);
$config_end_time = strtotime($appconf->end_time);
$current_time = strtotime($conf_execution_timer);
if($appconf->start_h == $conf_execution_meridiem){ /*AM-PM*/
if($config_start_time >= $current_time){ /*Check if the start time is now o still active*/
$conf_execution_proccess = true;
}
}
if($appconf->end_h == $conf_execution_meridiem){ /*AM-PM*/
if($config_end_time >= $current_time){ /*Check if the time has ended*/
$conf_execution_proccess = false;
}
}
if($conf_execution_proccess == true){
echo 'YES THE GAME IS STILL ACTIVE';
}else{
echo "NO GAME HAS ENDED";
}
}
?>
Assuming your start_date column is type VARCHAR():
Assuming your end_time is always later in the day than your start_time:
You can use this sort of query to get a starting and ending DATETIME value for each row of your table. It glues together the date, the time, and the AM/PM fields and converts them to DATETIME. (http://sqlfiddle.com/#!9/2d84ea/1/0)
SELECT game_id,
STR_TO_DATE(CONCAT(start_date,'-',start_t,start_h), '%m/%d/%Y-%h:%i%p') start_ts,
STR_TO_DATE(CONCAT(start_date,'-',end_time,end_h), '%m/%d/%Y-%h:%i%p') end_ts,
status
FROM game_config
Then you can compare those start_ts and end_ts values to NOW() by appending this line to your query
Then you can fill out your query with the criteria you need, giving this query.
SELECT game_id,
STR_TO_DATE(CONCAT(start_date,'-',start_t,start_h), '%m/%d/%Y-%h:%i%p') start_ts,
STR_TO_DATE(CONCAT(start_date,'-',end_time,end_h), '%m/%d/%Y-%h:%i%p') end_ts,
status
FROM game_config
WHERE game_id = : gameid AND status = 0
HAVING start_ts <= NOW() and NOW() < end_ts
LIMIT 1
But, your start and end times sometimes span midnight, which means your end_ts needs to be on the next day. That makes your date-checking logic quite a bit trickier. You'll have to do some sort of conditional expression in which you do + INTERVAL 1 DAY to your end_ts if it is before your start_ts.
You will be much better off (#eggyal pointed this out in his comment) if you put the following columns in your table.
game_id int
start_ts timestamp of start
end_ts timestamp of end
status int
Then you don't have to worry about all the stuff about making a proper timestamp from three different columns, and you don't have to do anything special when the start and end times span midnight.
And, this will work intuitively no matter what timezone your users are in.

Improve the speed of this MYSQL query

I am using PHP and MYSQL to graph call concurenncy from an Asterisk CDR database,
I currently use the following prepared statement:
$query=$cdrdb->prepare('select count(acctid) from cdr where calldate between ? and ? or DATE_ADD(calldate, INTERVAL duration SECOND) between ? and ?');
and then the following foreach loop to enter the variables:
foreach ($timerange as $startdatetime){
$start=$startdatetime->format("Y-m-d H:i:s");
$enddatetime=new DateTime($start);
$enddatetime->Add($interval);
$end=$enddatetime->format("Y-m-d H:i:s");
if(!$query->execute(array($start, $end, $start, $end))){
echo "Execute failed: (" . $stmt->errno . ") " . $stmt->error;
}
if (!($res = $query->fetchall())) {
echo "Getting result set failed: ";
}
array_push($callsperinterval,$res[0][0]);
}
Timerange can be every hour for a day, every day for a month or every week for a year.
the calldate column is marked as an index column.
The table currently holds 122000 records.
the result of running an EXPLAIN on the query:
mysql> explain select count(acctid) from cdr where calldate between '2014-10-02 23:30:00' and '2014-11-03 00:00:00' or DATE_ADD(calldate, INTERVAL duration SECOND) between '2014-10-02 23:30:00' and '2014-11-03 00:00:00';
+----+-------------+-------+------+---------------+------+---------+------+--------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+--------+-------------+
| 1 | SIMPLE | cdr | ALL | calldate | NULL | NULL | NULL | 123152 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+--------+-------------+
A single run of the query takes around 0.14s so for a 24 hour period with an hourly interval the script should finish in about 3.36 seconds, but it ends up taking about 12 seconds
Currently the whole process can take up to 20 seconds to run for a 24 hour period,can anyone please help me to improve the speed of this query?
This part is the bottleneck in your query:
DATE_ADD(calldate, INTERVAL duration SECOND)
This is because MySQL is performing "math" on each row of the first subset determined from your first WHERE condition every row on your entire table that didn't match the first part of your WHERE statement since you are using WHERE OR, not WHERE AND.
I assumed your table looks something a little like:
acctid | calldate | duration
========================================
1 | 2014-12-01 17:55:00 | 300
... etc.
Consider rewriting your schema such that you are not using intervals that MySQL must calculate for each row, but full DateTime columns that MySQL can perform immediate comparisons on:
acctid | calldate | duration_end
==================================================
1 | 2014-12-01 17:55:00 | 2014-12-01 18:00:00
To rewrite this schema, you can make that new column and then do (this may take a while to process but will serve you well in the long run):
UPDATE cdr SET duration_end = DATE_ADD(calldate, INTERVAL duration SECOND);
Then scrap the duration column and rewrite your application to save into the new column!
Your resulting query will be:
select count(acctid) from cdr where calldate > ? and (calldate < ? or duration_end between ? and ?)
Assuming that nothing can change in the schema, then you're stuck with that function. However, you can try having MySQL work with subsets so that it's not doing math on so many rows:
select
count(acctid)
from
cdr
where
calldate > ? and
(calldate < ? or DATE_ADD(calldate, INTERVAL duration SECOND) between ? and ?)
I can't guarantee much of a performance increase from this solution although it may be a noticeable one depending on your data set.
For asterisk cdrs you can just do like this
Let say you used:
$query=$cdrdb->prepare('select count(acctid) from cdr where calldate between ? and ? or DATE_ADD(calldate, INTERVAL duration SECOND) between ? and ?');
$query->execute(array($start, $end, $start, $end))
You have use like this
$query=$cdrdb->prepare('select count(acctid) from cdr where calldate between ? and DATE_ADD(?, interval ? SECOND) and (calldate between ? and ? or DATE_ADD(calldate, INTERVAL duration SECOND) between ? and ?)
');
$MAX_CALL_LENGHT_POSIBLE = 60*60*10; # usualy 10 hr is not reachable on most calls. If you limit it in call, you can decrease to even less values
$query->execute(array($start, $end,$MAX_CALL_LENGHT_POSIBLE,$start,$end $start, $end))
So just first limit query to interval where that stop_time can be.
But much simple will be add column call_end_time and create trigger
DROP TRIGGER IF EXISTS cdr_insert_trigger;
DELIMITER //
CREATE TRIGGER cdr_insert_trigger BEFORE INSERT ON cdr
FOR EACH ROW BEGIN
Set NEW.call_end_time=DATE_ADD(OLD.calldate,interval OLD.duration second);
END//
DELIMITER ;
Sure you need create index on BOTH calldate and call_end_time column and use Union instead of OR(otherwise one part will not use index)
If disk space is less important than speed, try:
ALTER TABLE cdr ROW_FORMAT = FIXED;

writing a row for each day. mySQL

i've a web form, the user input two dates (YYYY/MM/DD) representing a interval and other informations about an activity on a day.
EXAMPLE: he select 2013/09/12 to 2013/10/02 activity: Swimming
in that case my table will look like:
date | activity
2013/09/12 | swimming
2013/09/13 | swimming
2013/09/14 | swimming
2013/09/15 | swimming
2013/09/16 | swimming
and so on....
i've to write a row with the activity for every day of the date inveral provided by the user
can i do this without iterate through days manually?
i'm using php+mysqli
EDIT: i can't use two columns for start and end date
I think it is better to make two columns (e.g start_day & end_day).
If it is not possible to change your DB construction you can try this:
PHP:
$last_date = $start_date;
$ready = false;
while ($ready === false) {
$last_date = date('Y-m-d', strtotime($last_date . ' + 1 day'));+
// add $last_date to your mysql table here
if ($last_date == $stop_date) {
$ready = true;
}
}
It's untested but I hope it works ;)
Use 2 different date columns in your table; one for the start date and one for the end date, you can then add the beginning and end dates of the activity to each of these respectively.
i can give You a clue if You can use PROCEDURES:
DELIMITER $$
DROP PROCEDURE IF EXISTS `dowhile_thing`$$
CREATE DEFINER=`root`#`localhost` PROCEDURE `dowhile_thing`(dt_start DATE,dt_stop DATE, thing CHAR(20))
BEGIN
DECLARE v1 INT DEFAULT 0;
CREATE TEMPORARY TABLE tmp (
`day_name` VARCHAR(256) DEFAULT NULL,
`date_change` DATE DEFAULT NULL,
thing CHAR(20)
) ENGINE=MYISAM DEFAULT CHARSET=utf8 ;
SELECT TIMESTAMPDIFF(DAY, dt_start, dt_stop)+1 INTO v1;
WHILE v1 > -1 DO
INSERT tmp (SELECT DAYNAME( ADDDATE(dt_start, INTERVAL v1 DAY) ),ADDDATE(dt_start, INTERVAL v1 DAY),thing );
SET v1 = v1 - 1;
END WHILE;
SELECT * FROM tmp;
DROP TEMPORARY TABLE IF EXISTS tmp;
END$$
DELIMITER ;
and then call:
CALL dowhile_thing('2013-06-10','2013-06-14','swim');
result:
day_name | date_change | thing
Saturday | 2013-06-15 | swim
Friday | 2013-06-14 | swim
...

unix_timestamp date and time difference

I have stored three essential timestamps and times into my database table, format as follows:
mysql> select receivedtime, requesttime, sla from table;
+---------------------+---------------------+----------+
| receivedtime | requesttime | sla |
+---------------------+---------------------+----------+
| 2013-05-26 22:37:04 | 2013-05-26 12:37:04 | 02:59:59 |
| 2013-05-26 14:36:44 | 2013-05-21 12:39:09 | 72:00:00 |
+---------------------+---------------------+----------+
2 rows in set (0.00 sec)
I need to put a few conditions like below:
difference = (receivedtime - requesttime);
if [difference <= sla]
{
show meet
} else {
show don't meet
}
legend:
receivedtime [timestamp] as because days
requesttime [timestamp] as because days
sla [time] as because hour
I have checked UNIX_TIMESTAMP() of mysql, strtotime() of php and few other tries.
I also have checked several threads # stackoverflow.com, but I can't find proper solution.
Any ideas...!
Check TIMESTAMPDIFF() and SEC_TO_TIME() functions:
SELECT
`receivedtime`,
`requesttime`,
`sla`,
(SEC_TO_TIME(TIMESTAMPDIFF(SECOND, `requesttime`, `receivedtime`)) < `sla`) as `meet`
FROM
`table`;
Try
SELECT receivedtime,
requesttime,
CASE WHEN TIMESTAMPDIFF(SECOND, requesttime, receivedtime) < TIME_TO_SEC(sla)
THEN 'meet' ELSE 'don\'t meet' END sla
FROM table1
Here is SQLFiddle demo
Try selecting from your database like this ...
select TIMESTAMPDIFF(SECOND, receivedtime, requesttime) as difference, TIME_TO_SEC(sla) as slasecs from table
and then you can do your.
if [difference <= sla]
{
show meet
}
else
{
show don't meet
}
That is a datetime field, not a timestamp field. You should start with reading up on what a timestamp is.
you can figure out the difference by converting to timestamp then subtracting.
$recieved = strtotime($res['receivedtime']);
$requested = strtotime($res['requesttime']);
$difference = $recieved - $requested;
if($difference <= $sla){
//do stuff
}
You can do that in SQL
SELECT (UNIX_TIMESTAMP(receivedtime) - UNIX_TIMESTAMP(requesttime)) AS difference FROM table
it gives you the difference in seconds.
Try this
select TIMESTAMPDIFF(SECOND,`requesttime`,`receivedtime`) as timeDiff FROM `table`
WHERE TIMESTAMPDIFF(SECOND,`requesttime`,`receivedtime`) > TIME_TO_SEC(`sla`)
SEE SQL FIDDLE
You can find relevant in mysql documentation

Comparing current date/time with date/time in a database

I need to create something to display an image based on the current date and time. I have an SQL table set up that looks like this:
eventid | startdate | enddate | imgfile | status
6 | 2013-04-29 | 2013-05-03 | finals.jpg | active
What I need to do is compare the start date and end date to the current date, and if the current date falls within the startdate and enddate, then display the imgfile.
I'm not sure how to go about this, but this is what I tried.
//get current date and time
$currentdatetime = strtotime(now);
$formatteddatetime = date("y-m-d", $currentdatetime);
while($row=mysql_fetch_array($getimage))
{
//get variable for startdate
$formattedstartdate = date("y-m-d", $row[startdate]);
$formattedenddate = date("y-m-d", $row[enddate]);
if($formatteddatetime >= $formattedstartdate AND $formatteddatetime <= $formattedenddate AND $row[status] == 'active')
{
echo $row[imgfile];
}
}
?>
Right now, it doesn't show anything inside of the entire while statement. Am I at least on the right track?
the CURRENT_DATE returns the current date in ‘YYYY-MM-DD’ format or YYYYMMDD format depending on whether numeric or string is used in the function. CURDATE() and CURRENT_DATE() are the synonym of CURRENT_DATE.
You should do it in your SQL query instead of PHP. For example:
SELECT * FROM table WHERE CURRENT_DATE BETWEEN startdate AND enddate;
CURRENT_DATE() is a MySQL function that returns the current date, as the name suggests. It is one of a few special functions that have an alias without the parentheses.

Categories