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
...
Related
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.
On my database table I have 2 columns, start_date and end_date.
Sample data would be:
-------------------------------
start_date | end_date
-------------------------------
2017-11-01 2017-11-02
2017-11-03 2017-11-07
2017-11-20 2017-11-28
2017-11-13 2017-12-02
-------------------------------
I need to find if there are 5 consecutive days that are not yet used, which in this case, there is:
(2017-11-08 to 2017-11-13)
I'm using PHP and MySQL.
Thanks in advance!
You'd need to check for edge cases depending on your actual data and if there were no overlap dates, but this is a good start for the provided data.
Assuming table and data as defined as below:
CREATE TABLE
`appointments`
(
`appointment_id` INT PRIMARY KEY AUTO_INCREMENT,
`start_date` DATE,
`end_date` DATE
);
INSERT INTO
`appointments`
(`start_date`, `end_date`)
VALUES
('2017-11-01', '2017-11-02'),
('2017-11-03', '2017-11-07'),
('2017-11-20', '2017-11-28'),
('2017-11-13', '2017-12-02');
If you order the rows, and take the lag from the end date before it, and take any gaps of 5 or more. In SQL Server there are LAG functions, but here's a way of doing the same. Then once you have a table of all rows and their corresponding gaps, you take the start date of that period, and create the gap period from the number of days between. Since TIMESTAMPDIFF is inclusive, you need to subtract a day.
SET #end_date = NULL;
SELECT
DATE_ADD(`start_date`, INTERVAL -(`gap_from_last`-1) DAY) AS `start_date`,
`start_date` AS `end_date`
FROM
(
SELECT
`appointment_id`,
CASE
WHEN #end_date IS NULL THEN NULL
ELSE TIMESTAMPDIFF(DAY, #end_date, `start_date`)
END AS `gap_from_last`,
`start_date`,
#end_date := `end_date` AS `end_date` -- Save the lag date from the row before
FROM
`appointments`
ORDER BY
`start_date`,
`end_date`
) AS `date_gap` -- Build table that has the dates and the number of days between
WHERE
`gap_from_last` > 5;
Provides:
start_date | end_date
------------------------
2017-11-08 | 2017-11-13
Edit: Oops! Forgot the SQLFiddle (http://sqlfiddle.com/#!9/09cfce/16)
SELECT x.end_date + INTERVAL 1 DAY unused_start
, MIN(y.start_date) unused_end
FROM appointments x
JOIN appointments y
ON y.start_date >= x.end_date
GROUP
BY x.start_date
HAVING DATEDIFF(MIN(y.start_date),unused_start) >= 5;
i have been working on event schedule with php and MySQL my goal is to be able to have the background of a website change for each event such as Halloween Christmas and so on i have come up with one that will work with the month but i am needing it to workout the day to ignoring the year
<?php
$con = mysql_connect(MYSQL_host,MYSQL_username,MYSQL_password);
mysql_select_db(MYSQL_Database, $con);
$result = mysql_query('SELECT * FROM Event_Schedule WHERE MONTH(Start) <= MONTH(NOW()) AND MONTH(End) >= MONTH(NOW())') or die('Query failed: ' . mysql_error());
$Edit_theme_row = mysql_fetch_array($result)
?>
i have tried adding Day in to the code
$result = mysql_query('SELECT * FROM Event_Schedule WHERE (MONTH(Start) <= MONTH(NOW()) AND DAY(Start) <= DAY(NOW())) AND (MONTH(End) >= MONTH(NOW()) AND DAY(End) >= DAY(NOW()))') or die('Query failed: ' . mysql_error());
$Edit_theme_row = mysql_fetch_array($result)
?>
But seem to ignore event
using template DATE in MySQL
example
2015-10-28 to 2015-11-02 halloween
2015-12-01 to 2015-12-26 christmas
ignoring the year so each year it will change on that month and day
i hope that i understand your problem correctly. The first thing i have seen is that you use functions in the WHERE on the database fields. This is not a good idea. So MySQL must read every record (FULL TABLE SCAN) to do this and cant use an index for this.
The second thing is that you not normalize the start and end date of each event in 2 separate fields to do an easy compare. You can store the dates from the application in second fields with an normalized year ie. '1970' so you can easy compare it or you use PERSISTENT fields in MySQL then MySQL can do it for you.
Here a Sample
CREATE TABLE `table1` (
`nr` int(11) unsigned NOT NULL AUTO_INCREMENT,
`event_name` varchar(32) DEFAULT NULL,
`event_start` date NOT NULL DEFAULT '0000-00-00',
`event_end` date NOT NULL DEFAULT '0000-00-00',
`norm_start` date AS ( date_format(event_start,'1970-%m-%d') ) PERSISTENT,
`norm_end` date AS ( date_format(event_end,'1970-%m-%d') ) PERSISTENT,
PRIMARY KEY (`nr`),
KEY `event_start` (`event_start`,`event_end`),
KEY `norm_start` (`norm_start`,`norm_end`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Now we insert a row
INSERT INTO `table1`
( `event_name`, `event_start`, `event_end`)
VALUES
('hallo', '2015-10-31', '2015-10-31');
The Reseult
MariaDB > select * from table1;
+----+------------+-------------+------------+------------+------------+
| nr | event_name | event_start | event_end | norm_start | norm_end |
+----+------------+-------------+------------+------------+------------+
| 4 | hallo | 2015-10-31 | 2015-10-31 | 1970-10-31 | 1970-10-31 |
+----+------------+-------------+------------+------------+------------+
1 row in set (0.00 sec)
Now you can directly compare the dates
SELECT *
FROM table1
WHERE date_format(now(),'1970-%m-%d')
BETWEEN norm_start AND norm_end;
So you can the events. The only thing is when a event overlaps a year ( 2015-12-30 - 2016-01-07 ) you mus put 2 rows in the eventtable.
Please let me now if this wars what you want
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;
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.