Update a field based off of the current value (in mysql) - mysql

I have a table similar to
CREATE TABLE `mytable` (
`ID` int(11) unsigned NOT NULL AUTO_INCREMENT,
`Time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
KEY `time` (`Time`),
) ENGINE=MyISAM AUTO_INCREMENT=2373485 DEFAULT CHARSET=latin1
I had a weird issue with daylight savings time, and now I need to update rows with IDs 2370144 through 2373391 so that the Time values are six hours less than their current values.
I can select the affected rows with
SELECT * FROM mytable WHERE ID >= 2370144 AND ID <= 2373391
How do I update these entries so that the new timestamp is six hours less than the old value?

I think this will work
UPDATE mytable SET Time = date_sub(Time, INTERVAL 6 HOUR) WHERE id BETWEEN 2370144 AND 2373391;

UPDATE mytable
SET `Time` = (`Time` - INTERVAL 6 HOUR)
WHERE ID >= 2370144
AND ID <= 2373391
Expanding on this a little bit, when feasible I would typically run a SQL query to generate a .sql file that contains one update statement per row, then execute that sql file to update the rows. Since you are only updating about 3,000 rows this should be feasible for you.
This dump and load approach has a couple of benefits:
You can save the SQL script as an
audit record of what you changed.
You can include both the ID and the Time
value in the SQL script. That way if
you accidentally run the script more
than once you don't end up changing
the value to something incorrect. For
example, if you ran my original
update twice the values would end up
6 hours too low, but if you use the
dump-and-load approach and run the
script twice, the second time it
won't change the records because the
where clause will no longer match.
Here's an example of the dump-and-load approach:
select concat('update mytable set `Time` = ''',
`Time` - interval 6 hour,
''' where id = ',
id,
' and `Time` = ''',
`Time`,
''';') as sql_stmt
into outfile '/tmp/mytable.update.dstfix.20110315.sql'
from mytable
WHERE ID >= 2370144
AND ID <= 2373391;
\. /tmp/mytable.update.dstfix.20110315.sql

Related

Generate a text file based on comparison of two rows in MYSQL

I have a MYSQL table which contains timestamp and direction (buy/sell signal) of stock market data.
Below is the CREATE and INSERT statement of sample data.
The table is in descending order of timestamp, and the table is truncated and reinserted at 5-minute interval. I have included a id field which is autoincremented, as it may help in comparing the first row with the second row.
Everytime the direction of the market changes, I want a text file to be generated. As an example (from sample data), when timestamp was 15:00:00, since it was the first row that was inserted to the table, it should generate a text file as SELL.txt. At 15:05:00, since the direction changed from SELL to BUY, it should generate a text file as BUY.txt. Since the direction did not change at 15:10:00 and 15:15:00 compared to the previous row, no text file should be generated. At 15:20:00, since the direction changed from BUY to SELL, it should generate a text file as SELL.txt. Since the direction did not change at 15:25:00 and 15:30:00 compared to the previous row, no text file should be generated.
In Summary, if the cell value of the first row of direction field is not equal to the cell value of the second row of direction field, then a text file has to be generated based on the value of the first row of direction field. If the cell value of the first row of direction field is equal to the cell value of the second row of direction field, then no text file has to be generated.
I am assuming this can be implemented using stored procedures. However, I am new to stored procedures, and I have not been able to get this implemented so far. I would truly appreciate if someone can help in this regard.
thanks and regards,
CREATE TABLE `tbl` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`timestamp` datetime DEFAULT NULL,
`direction` varchar(10) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
INSERT INTO `market`.`tbl`
(`id`,
`timestamp`,
`direction`)
VALUES
(1,'2020-02-24 15:30:00','BUY'),
(2,'2020-02-24 15:25:00','SELL'),
(3,'2020-02-24 15:20:00','SELL'),
(4,'2020-02-24 15:15:00','BUY'),
(5,'2020-02-24 15:10:00','BUY'),
(6,'2020-02-24 15:05:00','BUY'),
(7,'2020-02-24 15:00:00','SELL');
CREATE TRIGGER tr
AFTER INSERT
ON tbl
FOR EACH ROW
BEGIN
IF EXISTS ( SELECT 1
FROM tbl t1, tbl t2
WHERE t1.`timestamp` BETWEEN CURRENT_TIMESTAMP - INTERVAL 2 MINUTE
AND CURRENT_TIMESTAMP + INTERVAL 2 MINUTE
AND t2.`timestamp` BETWEEN CURRENT_TIMESTAMP - INTERVAL 7 MINUTE
AND CURRENT_TIMESTAMP - INTERVAL 3 MINUTE
AND t1.direction != t2.direction ) THEN
IF 'SELL' = ( SELECT direction
FROM tbl
ORDER BY `timestamp` DESC LIMIT 1 ) THEN
/* SELECT 1 INTO OUTFILE 'SELL.txt'; */
INSERT INTO service (txt) VALUES (CONCAT(CURRENT_TIMESTAMP, ' SELL'));
ELSE
/* SELECT 1 INTO OUTFILE 'BUY.txt'; */
INSERT INTO service (txt) VALUES (CONCAT(CURRENT_TIMESTAMP, ' BUY'));
END IF;
END IF;
END
fiddle
Execute the fiddle a lot of times - you'll see that the messages are generated when the directions in 2 last records differs, and not generated when the direcions are the same.
The problem - each insert (except the first one) generates an insertion into the service table (and OUTFILE creation if uncomment it) - but the second attempt to create OUTFILE (which already exists) will fail which will cause the whole insertion query fail. You must create some static mark (service table which stores the timestamp is safe - and check it with some clearance like in records checking, +/- 2 min. seems to be useful) which allows to identify that the file was already created during this INSERT, and do not try to create it one more time.

How to update timestamp values in column by adding a specific time (seconds) to the existing timestamp using mysql?

I am using mysql and pma. I have a table mytable and a column time, storing ~17K individual values, i.e. timestamps (integers).
I need to update each by adding 962758 to each timestamp. What does the SQL command for that look like?
SELECT (*) FROM `mytable` t1
UPDATE `mytable` SET time = + 962758
PROFIT? :)
Would you need a SELECT statement for that or does it work with UPDATE only?
I cant use php for that in this case.
Considering that it's TIMESTAMP datatype, you can say
UPDATE `mytable` SET time = time + INTERVAL 962758 seconds;
Per your comment, since it's of INT type; you can just do the addition likewise you are already doing.
UPDATE `mytable` SET `time` = `time` + 962758;
If the data is stored as a datetime value, then you simply can use:
select timestampadd(second, 962758, time)
If the value is a unix timestamp, then it is already in seconds, and you can just add 962758.
According to your comment the field is int
UPDATE `mytable`
SET time = time + 962758;
And no need select

Efficiently Update MySQL Records

Example Data:
PostID DateTime DataTimeProper UserID UserName IPAddress
1234567.page#00008912 07/25/2013 14:50:21 NULL 00000001 TestUser 127.0.0.1
2468012.page#04208002 07/28/2013 18:42:13 NULL 03209827 BobTest 127.0.0.2
I'm looking for the most efficient way to update every record in a table (millions) where the DateTimeProper column IS NULL with the value being inserted coming from a str_to_date of the DateTime column.
SELECT STR_TO_DATE(`DateTime`,'%m/%e/%Y %H:%i:%s');
It's simply
UPDATE yourTable SET DateTimeProper = STR_TO_DATE(`DateTime`,'%m/%e/%Y %H:%i:%s')
WHERE DateTimeProper IS NULL;
It takes as long as it takes and it's a one time operation anyway, right?
If you really insist, you can make yourself the trouble to update in chunks, like (assuming you have an auto_increment column named id or something)
UPDATE yourTable SET DateTimeProper = STR_TO_DATE(`DateTime`,'%m/%e/%Y %H:%i:%s')
WHERE DateTimeProper IS NULL
AND id BETWEEN 0 AND 10000;
and then
UPDATE yourTable SET DateTimeProper = STR_TO_DATE(`DateTime`,'%m/%e/%Y %H:%i:%s')
WHERE DateTimeProper IS NULL
AND id BETWEEN 10000 AND 20000;
and so on. Some people do that in the hope, that the chunks fit into memory, but in my opinion it's not worth the trouble. MySQL already does a good job at that.
Try this
UPDATE table SET DataTimeProper =
(SELECT STR_TO_DATE(DateTime,'%m/%e/%Y %H:%i:%s'))
WHERE DataTimeProper IS NULL
Although you should consider changing your DateTime column to be of type DATETIME instead of string, as this will make it more efficient. Especially as you're dealing with tables of multi-million rows.

A procedure or a loop

I've a table named messages where users of my local hub store their messages(kind of like a web-forums). Currently, a majority of users are participating and I get nearly 30 to 50 new entries to my table everyday.
Since this has been going on for past few years, we've got nearly 100,000 rows of data in table. The table structure is kind of like this. Where fid is the PRIMARY and ip and id(nickname) are just INDEX.
I was using this kind of query uptil now; and then iterating the resultset in luasql as shown in this link. This, according to me, consumes a lot of time and space(in buffers).
`msg` VARCHAR(280) NOT NULL,
`id` VARCHAR(30) NOT NULL,
`ctg` VARCHAR(10) NOT NULL,
`date` DATE NOT NULL COMMENT 'date_format( %m/%d/%y )',
`time` TIME NOT NULL COMMENT 'date_format( %H:%i:%s )',
`fid` BIGINT(20) NOT NULL AUTO_INCREMENT,
`ip` CHAR(39) NOT NULL DEFAULT '127.0.0.1'
My problem is that now-a-days, we've switched to new API of PtokaX and the number of requests to read and write have increased dramatically. Since, I recently read about MySQL procedures, I was thinking if these procedures are a faster or safer way of dealing with this situation.
SELECT *
FROM ( SELECT *
FROM `messages`
ORDER BY `fid` DESC
LIMIT 50 ) AS `temp`
ORDER BY `fid` ASC;
P.S.
We get around one request to read one message every 7 to 10 seconds on average. On weekends, it rises to around one every 3 seconds.
Please let me know if anything more is required.
TO SUM UP
Is their a way that I can call a stored procedure and get the final result in a smaller time. Current query(and method) takes it nearly 3 seconds to fetch and organize the data.
Few things regarding your query:
SELECT *
FROM ( SELECT *
FROM `messages`
ORDER BY `fid` DESC
LIMIT 50 ) AS `temp`
ORDER BY `fid` ASC;
Never SELECT * (all); always specify a column list (what you need)
Subqueries typically cost more (for sorting & storage)
If you are trying to fetch the bottom '50', trying using a BETWEEN clause instead
You can always see what you're query is doing by using EXPLAIN. I would try the following query:
SELECT `msg`, `id`, `ctg`, `date`, `time`, `fid`, `ip` FROM `messages`
WHERE `fid` > (SELECT MAX(`fid`)-50 FROM `messages`)
ORDER BY `fid`

Mysql minimum function

Is there a predefined MySQL function that returns minimum of its arguments' values (MINIMUM(1,16) -> 1)?
To be more specific, I have a time-on-site column in one of my mysql tables.
Every visitor polls my server every 30 sec making an update:
UPDATE `mytable` SET `lastUpdate` = NOW() WHERE `id` = ?;
but I'd like to update also timeOnSite column like this:
UPDATE `mytable` SET `timeOnSite` = (
`timeOnSite` + MINIMUM(
TIMESTAMPDIFF(SECOND, lastUpdate, NOW()), 30
)
),
`lastUpdate` = NOW() WHERE `id` = ?;
But the problem is that there are no such MINIMUM function, and I failed to find it in MySQL manuals.
That's because its called LEAST() to avoid confusion with the aggregate function MIN().