Get difference between TIMESTAMPs in MySQL and update another Column - mysql

I have 2 columns in MySQL db with datatype as TIMESTAMP.
For every row I want to store the difference between the these 2 timestamps in hh:mm:ss (hh might exceed 24 or might also be 3 digits) into another column.
What should I use for this?
I tried this but it is not working.
update ABC_TABLE set colC=TIMEDIFF(colA, colB);
colC is of Type TIME
|Field | Type | Null | Key | Default | Extra
+------------------------+--------------+------+-----+---------+-------+
| colC | time | YES | | NULL |

The hour part of the time datatype can't hold values larger than 23. You'll have to use char or varchar.
Calculate the difference between the two timestamps in seconds, like this:
SELECT TIMESTAMPDIFF(SECOND, '2016-06-10 11:00:00', '2016-06-10 12:00:00');
Then you can use this function to convert the seconds into the format days:hours:minutes:seconds
DROP FUNCTION IF EXISTS f_s2dhms;
DELIMITER $$
CREATE FUNCTION f_s2dhms(v_seconds INT)
RETURNS VARCHAR(15)
DETERMINISTIC
COMMENT 'converts seconds in days:hours:minutes:seconds'
BEGIN
DECLARE v_dhms VARCHAR(15);
DECLARE v_days INT;
DECLARE v_hours INT;
DECLARE v_minutes INT;
SET v_hours = v_seconds/3600;
SET v_minutes = (v_seconds % 3600) / 60;
SET v_seconds = (v_seconds % 3600) % 60;
IF (v_hours > 23) THEN
SET v_days = v_hours / 24;
SET v_hours = v_hours % 24;
ELSE
SET v_days = 0;
END IF;
RETURN CONCAT(v_days, ':', RIGHT(CONCAT('0', v_hours), 2), ':', RIGHT(CONCAT('0', v_minutes), 2), ':', RIGHT(CONCAT('0', v_seconds), 2));
END $$
DELIMITER ;
Use it like
SELECT f_s2dhms(TIMESTAMPDIFF(SECOND, '2016-06-10 11:00:00', '2016-06-10 12:00:00'));
here you can read more about timestampdiff()

Related

MySQL CONCAT DOUBLE values in trigger

UPDATE
The question needs clarity.
I'm converting converting milliseconds to hours minutes seconds to create a date independent timestamp.
Given time in milliseconds, I want the output to be a string in H:M:S
E.g.
9999999ms = 2.7777775 hrs
.7777775hrs * 60 = 46.66665 min
.66665min * 60 = 39.999sec
Desired output 02:46:39.999
This has nothing to do with TIMESTAMP. It is a simple calculation followed by a string concatenation.
I'm having a frustrating time with the CONCATENATION. IT only returns hours and it does not ROUND the returned value.
DECLARE v_timestamp VARCHAR(8);
DECLARE v_hours DOUBLE(5,3);
DECLARE v_minutes DOUBLE(5,3);
DECLARE v_seconds DOUBLE(5,3);
SET v_hours = (NEW.amount_viewed_ms)/(3600000);
SET v_minutes = (v_hours - FLOOR(v_hours)) * 60;
SET v_seconds = (v_minutes - FLOOR(v_minutes)) * 60;
SET v_timestamp = CONCAT(ROUND(v_hours), ":", ROUND(v_minutes), ":", ROUND(v_seconds));
SET NEW.timestamp = v_timestamp;
I could use another pair of eyes to help figure out where this is going wrong.
I really don't see your problem simply increasing a couple of field sizes seems to produce a desired result without an example of NEW.amount_viewed_ms it's not possible to say more
DROP PROCEDURE IF EXISTS P;
DELIMITER $$
CREATE PROCEDURE P()
BEGIN
DECLARE v_timestamp VARCHAR(20);
DECLARE v_hours double(20,3);
DECLARE v_minutes DOUBLE(5,3);
DECLARE v_seconds DOUBLE(5,3);
select (UNIX_TIMESTAMP(CONCAT(DATE(NOW()), ' ', CURTIME(3))))/(3600000);
SET v_hours = (UNIX_TIMESTAMP(CONCAT(DATE(NOW()), ' ', CURTIME(3))))/(3600000);
SET v_minutes = (v_hours - FLOOR(v_hours)) * 60;
SET v_seconds = (v_minutes - FLOOR(v_minutes)) * 60;
select v_hours,v_minutes,v_seconds, (UNIX_TIMESTAMP(CONCAT(DATE(NOW()), ' ', CURTIME(3))))/(3600000);
SET v_timestamp = CONCAT(ROUND(v_hours), ":", ROUND(v_minutes), ":", ROUND(v_seconds));
select v_timestamp;
END $$
DELIMITER ;
CALL P();
MariaDB [sandbox]> call p();
+------------------------------------------------------------------+
| (UNIX_TIMESTAMP(CONCAT(DATE(NOW()), ' ', CURTIME(3))))/(3600000) |
+------------------------------------------------------------------+
| 457.2335910 |
+------------------------------------------------------------------+
1 row in set (0.002 sec)
+---------+-----------+-----------+------------------------------------------------------------------+
| v_hours | v_minutes | v_seconds | (UNIX_TIMESTAMP(CONCAT(DATE(NOW()), ' ', CURTIME(3))))/(3600000) |
+---------+-----------+-----------+------------------------------------------------------------------+
| 457.234 | 14.040 | 2.400 | 457.2335910 |
+---------+-----------+-----------+------------------------------------------------------------------+
1 row in set (0.025 sec)
+-------------+
| v_timestamp |
+-------------+
| 457:14:2 |
+-------------+
1 row in set (0.032 sec)

Return value from DATE_SUB/DATE_ADD in stored function

I am trying to write a stored function that takes a string in proper ISO format (yyyy-mm-dd) and subract a certain number of weekdays from it. Based off of this question here I have tried both the accepted answer as well as one a few answers down that does things differently, however, both of them are just saying how to write the pure sql, and don't have an example of a function.
What I currently have is this:
delimiter //
CREATE DEFINER=`root`#`localhost` FUNCTION `WEEKDATE_SUB` (days TINYINT, date_val VARCHAR(16))
RETURNS DATE DETERMINISTIC
BEGIN
DECLARE SUBVAL INT;
DECLARE dow INT;
CASE
WHEN dow=1 THEN SET SUBVAL = (days +(FLOOR((days-0.5)/5)+1)*2 - 1);
WHEN dow=2 THEN SET SUBVAL = (days +(FLOOR((days-0.5)/5)+1)*2);
WHEN dow=3 THEN SET SUBVAL = (days-1 +(FLOOR(((days-1)-0.5)/5)+1)*2 + 1);
WHEN dow=4 THEN SET SUBVAL = (days-2 +(FLOOR(((days-2)-0.5)/5)+1)*2 + 2);
WHEN dow=5 THEN SET SUBVAL = (days-3 +(FLOOR(((days-3)-0.5)/5)+1)*2 + 3);
WHEN dow=6 THEN SET SUBVAL = (days-4 +(FLOOR(((days-4)-0.5)/5)+1)*2 + 4);
WHEN dow=7 THEN SET SUBVAL = (days-5 +(FLOOR(((days-5)-0.5)/5)+1)*2 + 5);
END CASE
RETURN DATE_SUB(date_val, INTERVAL SUBVAL DAY);
END;//
When I try to add it, I get a vague error (as mysql is fond of offering):
You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'RETURN DATE_SUB(date_val, INTERVAL SUBVAL DAY);
END' at line 14
I have tried several variations on returning including trying to define a variable for Date sub and return that, but it's pretty much the same error.
Outside of a function, I know this works, so it seems like I should just be able to return that.
SELECT DATE_SUB("2016-01-01", INTERVAL 4 DAY);
The docs suggest you need to have END CASE, instead of END
Additionally, the stored function will probably execute a little faster if you use the CASE expr WHEN value2 THEN .... WHEN value2 THEN ... END CASE version, since it will not have to repeat the DAYOFWEEK function calls potentially 7 times.
you can also use something like this
delimiter //
CREATE DEFINER=`root`#`localhost` FUNCTION `WEEKDATE_SUB` (date_val VARCHAR(10), days TINYINT)
RETURNS VARCHAR(10) DETERMINISTIC
BEGIN
RETURN date_val - INTERVAL
FLOOR(days/5)*7 +
IF(DAYOFWEEK(date_val)-1 <= days - FLOOR(days/5)*5
, (days - FLOOR(days/5)*5)+2
, days - FLOOR(days/5)*5
) DAY;
END;//
samples
mysql> SELECT WEEKDATE_SUB('2017-02-06',1);
+------------------------------+
| WEEKDATE_SUB('2017-02-06',1) |
+------------------------------+
| 2017-02-03 |
+------------------------------+
1 row in set (0,00 sec)
mysql> SELECT WEEKDATE_SUB('2017-02-07',1);
+------------------------------+
| WEEKDATE_SUB('2017-02-07',1) |
+------------------------------+
| 2017-02-06 |
+------------------------------+
1 row in set (0,00 sec)
mysql> SELECT WEEKDATE_SUB('2017-02-07',2);
+------------------------------+
| WEEKDATE_SUB('2017-02-07',2) |
+------------------------------+
| 2017-02-03 |
+------------------------------+
1 row in set (0,00 sec)
mysql>
This is what I finally needed to get this working. Notice, the ; and the change to how I am using the CASE statement.
delimiter //
CREATE DEFINER=`root`#`localhost` FUNCTION `WEEKDATE_SUB` (date_val VARCHAR(10), days TINYINT)
RETURNS VARCHAR(10) DETERMINISTIC
BEGIN
DECLARE SUBVAL INT;
DECLARE dow INT;
SET dow = DAYOFWEEK(date_val);
CASE dow
WHEN 1 THEN SET SUBVAL = (days +(FLOOR((days-0.5)/5)+1)*2 - 1);
WHEN 2 THEN SET SUBVAL = (days +(FLOOR((days-0.5)/5)+1)*2);
WHEN 3 THEN SET SUBVAL = (days-1 +(FLOOR(((days-1)-0.5)/5)+1)*2 + 1);
WHEN 4 THEN SET SUBVAL = (days-2 +(FLOOR(((days-2)-0.5)/5)+1)*2 + 2);
WHEN 5 THEN SET SUBVAL = (days-3 +(FLOOR(((days-3)-0.5)/5)+1)*2 + 3);
WHEN 6 THEN SET SUBVAL = (days-4 +(FLOOR(((days-4)-0.5)/5)+1)*2 + 4);
WHEN 7 THEN SET SUBVAL = (days-5 +(FLOOR(((days-5)-0.5)/5)+1)*2 + 5);
ELSE SET SUBVAL = days;
END CASE;
RETURN DATE_SUB(date_val, INTERVAL SUBVAL DAY);
END;//

Mysql: Finding longest character sequence in a string

In mySQL, how can I find the length of the longest sequence of a given character? For example, in the following string
1325******2h3n***3k2n*
If I were looking for the * character, the result should be 6 because the chain of 6 * characters is the longest present in the string.
You can use instr and and generated table with UNION to get it.
-- This query can find up to 10. If more need, need to update the `UNION`.
select max((instr('1325*****2h3n***3k2n*',repeat('*', times)) != 0) * times ) longest_seq
from (select 1 times union select 2 union select 3 union select 4 union select 5
union select 6 union select 7 union select 8 union select 9 union select 10) t;
Demo:
mysql> select max((instr('1325*****2h3n***3k2n*',repeat('*', times)) != 0) * times ) longest_seq
-> from (select 1 times union select 2 union select 3 union select 4 union select 5
-> union select 6 union select 7 union select 8 union select 9 union select 10) t;
+-------------+
| longest_seq |
+-------------+
| 5 |
+-------------+
1 row in set (0.01 sec)
what your looking for is basically the length of the longest substring,
you can find the algorithm for it here
Trying to achieve this with a query would not be such a good idea,
I suggest, using a stored procedure instead.
Dylan Su's solution is clever and works well if you know the maximum number of characters is small or don't want the overhead of building a function.
On the other hand one of the following function definitions will work regardless of character length without having to add new UNION statements indefinitely.
This function loops over each of the characters in the string, and if they match the repeat character, increments a length counter. It then returns the max length.
DELIMITER //
CREATE FUNCTION LONGEST_CHARACTER_SEQUENCE(input VARCHAR(255), repeat_character CHAR(1))
RETURNS TINYINT UNSIGNED DETERMINISTIC NO SQL
BEGIN
DECLARE max_length TINYINT UNSIGNED DEFAULT 0;
DECLARE length TINYINT UNSIGNED DEFAULT 0;
DECLARE in_sequence BOOLEAN DEFAULT 0;
DECLARE position INT DEFAULT 1;
WHILE position <= LENGTH(input) DO
IF SUBSTRING(input, position, 1) = repeat_character THEN
IF in_sequence THEN
SET length = length + 1;
ELSE
SET length = 1;
END IF;
IF length > max_length THEN
SET max_length = length;
END IF;
SET in_sequence = 1;
ELSE
SET in_sequence = 0;
END IF;
SET position = position + 1;
END WHILE;
RETURN max_length;
END//
DELIMITER ;
SELECT LONGEST_CHARACTER_SEQUENCE('1325******2h3n***3k2n*', '*');
-- Returns: 6
Inspired by Dylan Su's answer, this function increments a length counter until INSTR no longer returns true. I think it's simpler.
DELIMITER //
CREATE FUNCTION LONGEST_CHARACTER_SEQUENCE(input VARCHAR(255), repeat_character CHAR(1))
RETURNS TINYINT UNSIGNED DETERMINISTIC NO SQL
BEGIN
DECLARE length TINYINT UNSIGNED DEFAULT 0;
WHILE INSTR(input, REPEAT(repeat_character, length + 1)) DO
SET length = length + 1;
END WHILE;
RETURN length;
END//
DELIMITER ;
SELECT LONGEST_CHARACTER_SEQUENCE('1325******2h3n***3k2n*', '*');
-- Also returns: 6

Count length of a record stripping out html tags

I am trying to get a grand total of review length, but people are allowed to add lists, tables, color, etc. I would just like to get the count of the actual review characters. Is there a way to use regex to strip out everything other than the real words to count the characters? I am using mySQL 5.5 and here is the current query I have:
select sum(COALESCE(length(reviewTxt),0) + COALESCE(length(reviewTxt1),0) + COALESCE(length(reviewTxt2),0) + COALESCE(length(reviewTxt3),0) + COALESCE(length(reviewTxt4),0) + COALESCE(length(reviewTxt5),0) + COALESCE(length(reviewTxt6),0) + COALESCE(length(reviewTxt7),0) + COALESCE(length(reviewTxt8),0) + COALESCE(length(reviewTxt9),0) + COALESCE(length(reviewTxt10),0)) as totalLength
from set_reviews
The following was taken from http://www.sitepoint.com/forums/showthread.php?656167-PHP-s-strip_tags%28%29-equivalent-MYSQL-function :
-=-=-=-=-=-=-=-=-=-=-=-=-
This is the php function strip_tags
delimiter ||
DROP FUNCTION IF EXISTS strip_tags||
CREATE FUNCTION strip_tags( x longtext) RETURNS longtext
LANGUAGE SQL NOT DETERMINISTIC READS SQL DATA
BEGIN
DECLARE sstart INT UNSIGNED;
DECLARE ends INT UNSIGNED;
SET sstart = LOCATE('<', x, 1);
REPEAT
SET ends = LOCATE('>', x, sstart);
SET x = CONCAT(SUBSTRING( x, 1 ,sstart -1) ,SUBSTRING(x, ends +1 )) ;
SET sstart = LOCATE('<', x, 1);
UNTIL sstart < 1 END REPEAT;
return x;
END;
||
delimiter ;
mysql> SELECT strip_tags('<B>Hi, mate!</B>') as strip_tags;
+------------+
| strip_tags |
+------------+
| Hi, mate! |
+------------+
1 row in set (0.00 sec)

MySQL add timestamp values

I have a table in MySQL with this format: (time = timestamp on insert)
id | tid | uid | time
31 | 1 | 14 | 2011-05-19 05:42:37 //start timestamp)
41 | 1 | 14 | 2011-05-19 07:18:42 //stop timestamp)
45 | 1 | 14 | 2011-05-19 07:18:49 //start timestamp)
46 | 1 | 14 | 2011-05-19 07:28:42 //stop timestamp)
What I need is to make a select that adds the time differences like this
(41 - 31) + (46 - 45) (i'm using the id's instead of the actual time values to better understand what I need to do )
something like SELECT (something that does this) AS TotalTimeSpent WHERE tid = '1'
If you insist on using this table layout (and I really hope you change your mind, it is truly horrific), you can do it with cursors in a stored procedure.
Pseudo-code would be something like this:
CREATE PROCEDURE gettotaltime()
BEGIN
DECLARE total, curr, prev DATETIME;
DECLARE odd INT DEFAULT 1;
DECLARE done INT DEFAULT 0;
DECLARE c CURSOR FOR SELECT time FROM tbl;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
OPEN c;
read_loop: LOOP
FETCH c INTO curr;
IF odd=0 THEN
SET total=dateadd(total,datediff(curr,prev)); -- or something similar, I forget
END IF;
SET prev=curr;
SET odd=1-odd;
IF done THEN
LEAVE read_loop;
END IF;
END LOOP;
CLOSE c;
SELECT total;
END;