MySQL CONCAT DOUBLE values in trigger - mysql

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)

Related

How to split a result from select column in mySQL to a mutiple columns

I have column in MYSQL database would like to extract that column data and split it into multiple columns Here is a sample of data that I would like to split
```
{"1744":"1","1745":"1","1747":"1","1748":"1","1749":"1","1750":"1"}
{"1759":"1"}
{"47":"1","48":"Ehebr","49":"1479977045596.jpg"}
```
I would like to split that into two columns like so with the first data:
as you notice this data come in different lengths and would like to be able to split any length of data, had a look here [How to split a resulting column in multiple columns
but I don't think that is what i want the result I got there was like so would also like to trim all the other braces and quotes on the data. here is my code so far
```
SELECT combined,SUBSTRING_INDEX( combined , ':', 1 ) AS a,
SUBSTRING_INDEX(SUBSTRING_INDEX( combined , ':', 2 ),':',-1) AS b,
SUBSTRING_INDEX(SUBSTRING_INDEX( combined , ':', -2 ),':',1) AS c,
SUBSTRING_INDEX( combined , ':', -1 ) AS d
FROM tablefoo WHERE combined is not null;
```
If you can live with procedures and cursors
drop procedure if exists p;
delimiter //
CREATE DEFINER=`root`#`localhost` PROCEDURE `p`(
IN `instring` varchar(255)
)
LANGUAGE SQL
NOT DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
COMMENT ''
begin
declare tempstring varchar(10000);
declare outstring varchar(100);
declare c1 varchar(100);
declare c2 varchar(100);
declare checkit int;
declare done int;
DECLARE CUR1 CURSOR for SELECT t.col FROM T;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
drop table if exists occursresults;
create table occursresults (col1 varchar(20), col2 varchar(20));
open CUR1;
read_loop: LOOP
FETCH CUR1 INTO tempstring;
if done then leave read_loop; end if;
set tempstring = replace(tempstring,'{','');
set tempstring = replace(tempstring,'}','');
set tempstring = replace(tempstring,'"','');
set checkit = 0;
#select tempstring;
looper: while tempstring is not null and instr(tempstring,',') > 0 do
set checkit = checkit + 1;
if checkit > 100 then #In case of infinite loop
leave looper;
end if;
set outstring = substr(tempstring,1,instr(tempstring, ',') - 1);
set tempstring = ltrim(rtrim(replace(tempstring,concat(outstring,','),'')));
set c1 = substr(outstring,1,instr(outstring, ':') - 1);
set c2 = replace(outstring,concat(c1,':'),'');
INSERT INTO OCCURSRESULTS (COL1,COL2) VALUES (c1,c2);
# select tempstring,outstring,c1,c2;
end while;
#select tempstring;
set outstring = tempstring;
set c1 = substr(outstring,1,instr(outstring, ':') - 1);
set c2 = replace(outstring,concat(c1,':'),'');
INSERT INTO OCCURSRESULTS (Col1,Col2) VALUES (c1,c2);
end loop;
close cur1;
end //
delimiter ;
MariaDB [sandbox]> select * from t;
+---------------------------------------------------------------------+
| col |
+---------------------------------------------------------------------+
| {"1744":"1","1745":"1","1747":"1","1748":"1","1749":"1","1750":"1"} |
| {"1759":"1"} |
| {"47":"1","48":"Ehebr","49":"1479977045596.jpg"} |
+---------------------------------------------------------------------+
3 rows in set (0.00 sec)
MariaDB [sandbox]>
MariaDB [sandbox]> call p(1);
Query OK, 0 rows affected (0.65 sec)
MariaDB [sandbox]>
MariaDB [sandbox]> SELECT * FROM OCCURSRESULTS;
+------+-------------------+
| col1 | col2 |
+------+-------------------+
| 1744 | 1 |
| 1745 | 1 |
| 1747 | 1 |
| 1748 | 1 |
| 1749 | 1 |
| 1750 | 1 |
| 1759 | 1 |
| 47 | 1 |
| 48 | Ehebr |
| 49 | 1479977045596.jpg |
+------+-------------------+
10 rows in set (0.00 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;//

how to check if Integer array which record not Exist in mysql

REPORTER PARTNER NET_WEIGHT YEAR COMMODITY
'Egypt', 'Canada', '5', '2010', 'wheat'
'Germany', 'UK', '1', '2011', 'wheat'
'Mexico', 'France', '5', '2011', 'wheat'
This is my table i want to create one Procedure from which we want get data which is not Exist in table whose NET_WEIGHT not Exist int able
Like this i want when i pass input as "1,12,16"
then it should return 12|null and 16|null i have to check whose NET_WEIGHT is Not Found in Table and accordingly we should get NET_WEIGHT|null Data please suggest me how to do this
drop procedure if exists `tokensise`;
delimiter //
CREATE DEFINER=`root`#`localhost` procedure `tokensise`(`instring` varchar(255))
LANGUAGE SQL
NOT DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
COMMENT ''
begin
declare tempstring varchar(100);
declare outstring varchar(100);
declare checkit int;
set tempstring = ltrim(rtrim(instring));
set checkit = 0;
drop table if exists occursresults;
create table occursresults(letter char(1), occurs int);
looper: while tempstring is not null and instr(tempstring,',') > 0 do
set outstring = substr(tempstring,1,instr(tempstring, ','));
set tempstring = ltrim(rtrim(replace(tempstring,outstring,'')));
set outstring = replace(outstring,',','');
set checkit = checkit + 1;
insert into occursresults
select outstring, t.NET_WEIGHT
from (select outstring os) d left join t on t.NET_WEIGHT = d.os;
end while;
set outstring = tempstring;
set tempstring = ltrim(rtrim(replace(tempstring,outstring,'')));
set outstring = replace(outstring,',','');
set checkit = checkit + 1;
insert into occursresults
select outstring, t.NET_WEIGHT
from (select outstring os) d left join t on t.NET_WEIGHT = d.os;
end //
delimiter ;
result
MariaDB [sandbox]> call tokensise('1,2,5');
Query OK, 2 rows affected (0.38 sec)
MariaDB [sandbox]> select * from occursresults;
+--------+--------+
| letter | occurs |
+--------+--------+
| 1 | 1 |
| 2 | NULL |
| 5 | 5 |
| 5 | 5 |
+--------+--------+
4 rows in set (0.00 sec)
I would use 2 tables and compare one to another - I also use NOT IN rather than NOT EXIST - this sql I use on a football database that checks if teams exist in results table but not stats table. The code below will display all rows in results where hometeam does not exist as team.stats.
SELECT *
FROM results
WHERE hometeam NOT IN (SELECT team FROM stats)

Get difference between TIMESTAMPs in MySQL and update another Column

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()

Launch Trigger and Routine on Insert

Im attempting to have MySQL automatically insert data into another table after insert into one. I know to do this required Triggers and potentially Routines. I have a couple I've been trying to modify to do what I wish to accomplish but I appear to be hitting a dead end due to lack of experience, so help is greatly appreciated.
The table that has data inserted (db_tou_tracking):
tou_tracking_ID ICP_ID tou_tracking_start tou_tracking_units
----------------------------------------------------------------
2 2 2013-03-01 10.77
3 2 2013-03-01 11.00
There are a couple of other columns here, that separate out by time, but I'm interested by day, rather than time.
Table data should go into compounded. So as each of the above rows are inserted, it will either create a new row if the tou_tracking_start and ICP_ID do not exist, or update the existing row.
tou_tracking_daily_ID ICP_ID tou_tracking_start tou_tracking_units
------------------------------------------------------------------------------
1 2 2013-03-01 21.77
2 2 2013-03-02 25.36
Below is my Tigger (no errors when setup on MySQL, and it does appear to call when data is attempted to be inserted):
BEGIN
DECLARE presentcount INT;
SET presentcount = (SELECT count(*) FROM db_tou_tracking_daily WHERE tou_tracking_daily_day =
(SELECT tou_tracking_start FROM db_tou_tracking WHERE ICP_ID = db_tou_tracking_daily.ICP_ID ORDER BY tou_tracking_ID DESC)
);
IF (presentcount = 0) THEN
INSERT INTO db_tou_tracking_daily (ICP_ID, tou_tracking_daily_day, tou_tracking_start)
SELECT NEW.ICP_ID, NEW.tou_tracking_start, NEW.tou_tracking_units, calculate_units(NEW.ICP_ID, NEW.tou_tracking_start);
ELSE
UPDATE db_tou_tracking_daily SET tou_tracking_daily_units = calculate_units(NEW.ICP_ID, tou_tracking_daily_day)
WHERE ICP_ID = NEW.ICP_ID AND tou_tracking_daily_day = NEW.tou_tracking_start;
END IF;
END
and then the routine it calls to calculate units.
CREATE DEFINER=`root`#`localhost` FUNCTION `calculate_units`(ICP_ID INT, tou_tracking_daily_day DATE) RETURNS float
BEGIN
DECLARE units FLOAT;
DECLARE last_time DATE;
DECLARE last_watts INT;
DECLARE this_time DATETIME;
DECLARE this_watts INT;
DECLARE loop_done INT;
DECLARE curs CURSOR FOR
SELECT tou_tracking_timestart, tou_tracking_units FROM db_tou_tracking WHERE ICP_ID = ICP_ID AND tou_tracking_start = tou_tracking_daily_day ORDER BY tou_tracking_start DESC;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET loop_done = 1;
SET last_time = (SELECT max(tou_tracking_start) FROM db_tou_tracking WHERE ICP_ID = ICP_ID AND tou_tracking_start < tou_tracking_daily_day);
SET last_watts = (SELECT tou_tracking_units FROM db_tou_tracking WHERE ICP_ID = ICP_ID AND tou_tracking_start = last_time);
SET last_time = CAST(tou_tracking_start AS DATETIME);
SET loop_done = 0;
SET units = 0;
OPEN curs;
REPEAT
FETCH curs INTO this_time, this_watts;
IF last_watts IS NOT NULL THEN
SET units = units + (last_watts + this_watts);
END IF;
SET last_watts = this_watts;
SET last_time = this_time;
UNTIL loop_done END REPEAT;
CLOSE curs;
END
The routine throws back an error on line 3 when I try to run the SQL to setup the routine, but I can't see anything obviously wrong, but I'm not exactly sure what I'd be looking for.
Any help with this is hugely appreciated and any pointers that can be given along the way. Thanks :)
Attempting to replicate your issue, I'm going to guess the error you get is probably because you're not using a DELIMITER.
Executing a similar function creation statement I get the same error, and a syntax parse suggests it's not expecting the delimiter ;.
The one that causes an error on line 3.
CREATE DEFINER = 'root'#'localhost' FUNCTION test_func(foo INT) RETURNS FLOAT
BEGIN
DECLARE bar FLOAT;
RETURN 1;
END
Fixing it using delimiters.
DELIMITER $$
CREATE DEFINER = 'root'#'localhost' FUNCTION test_func(foo INT) RETURNS FLOAT
BEGIN
DECLARE bar FLOAT;
RETURN 1;
END$$
DELIMITER ;
If this does not fix your problem, are you able to provide a self contained function that doesn't rely on any of your existing tables, that also produces the same error so it can be tested?
create table t1 ( start date not null, units decimal(5,2) not null );
create table t2 ( start date not null, units decimal(5,2) not null );
delimiter //
create trigger trg1
after insert on t1
for each row
begin
update t2
set units = units + new.units
where start = new.start;
if ROW_COUNT() = 0 then
insert into t2
(start, units)
values (new.start, new.units);
end if;
end //
delimiter ; //
mysql> select * from t1;
Empty set (0.01 sec)
mysql> select * from t2;
Empty set (0.00 sec)
mysql> insert into t1 (start, units) values ('2014-01-01',100.02);
Query OK, 1 row affected (0.01 sec)
mysql> select * from t1;
+------------+--------+
| start | units |
+------------+--------+
| 2014-01-01 | 100.02 |
+------------+--------+
1 row in set (0.00 sec)
mysql> select * from t2;
+------------+--------+
| start | units |
+------------+--------+
| 2014-01-01 | 100.02 |
+------------+--------+
1 row in set (0.00 sec)
mysql> insert into t1 (start, units) values ('2014-01-01',200.05);
Query OK, 1 row affected (0.01 sec)
mysql> select * from t1;
+------------+--------+
| start | units |
+------------+--------+
| 2014-01-01 | 100.02 |
| 2014-01-01 | 200.05 |
+------------+--------+
2 rows in set (0.01 sec)
mysql> select * from t2;
+------------+--------+
| start | units |
+------------+--------+
| 2014-01-01 | 300.07 |
+------------+--------+
1 row in set (0.01 sec)