Forcing an alter table on MySQL - mysql

I'm trying to add a column to a live database, that's constantly being accessed:-
ALTER TABLE `projects` ADD COLUMN `cTime` INT(30) NULL DEFAULT NULL AFTER `session`
It ALWAYS hangs. This is a MyISAM table and I'm guessing it's trying to lock the table. I've tried IGNORE, but is there anyway to force this?

You have a double null in you statement
ALTER TABLE `projects` ADD COLUMN `cTime` INT(30) DEFAULT NULL AFTER `session`

Related

Auto Increment missing in my users table after importing on production. How should I modify it?

When I run this query :
ALTER TABLE `users` MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=56;
It gives me error like :
#1833 - Cannot change column 'id': used in a foreign key constraint
'designation_user_user_id_foreign' of table
'databasename.designation_user'
Found a solution that worked!
I made id column in users table unique using more option in action column of users table and then executed below query :
ALTER TABLE `users` MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT;

MySQL Online DDL changes completing, but not persisting

I have a very large table (> 6 bn rows & > 3tb of data) that I'm trying to alter.
Example schema:
CREATE TABLE `huge_table` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`col1` int(11) unsigned DEFAULT NULL,
`col2` int(11) unsigned DEFAULT NULL,
`col3` int(11) unsigned NOT NULL,
`col4` int(11) unsigned DEFAULT NULL,
`col5` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,
`col6` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`col7` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`col8` datetime GENERATED ALWAYS AS (cast(`created` as date)) VIRTUAL,
`dt1` timestamp(3) NULL DEFAULT NULL,
`dt2` timestamp(3) NULL DEFAULT NULL,
`dt3` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (`id`),
UNIQUE KEY `idx_col5` (`col5`),
KEY `col4` (`col4`),
KEY `col1` (`col1`),
KEY `col3` (`col3`),
KEY `col6` (`col6`,`col3`),
KEY `col2` (`col2`),
KEY `col8` (`col8`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
Most of these indexes are unnecessary, so I'm trying to clean them up, and add a more precise index. Note that all of the ALTER TABLE statements below take well over 24 hours to run.
First, I tried modifying all the indexes using Online DDL:
ALTER TABLE huge_table
DROP INDEX col4_idx,
DROP INDEX col1_idx,
DROP INDEX col3_idx,
DROP INDEX col6_idx,
DROP INDEX col2_idx,
DROP INDEX col8_idx,
ADD INDEX new_idx(col3, col6, col1),
ALGORITHM=INPLACE, LOCK=NONE;
When this finally finished (no errors), I checked the schema, only to find nothing had changed in the schema. Therefore, I decided to try again without forcing it to run "INPLACE":
ALTER TABLE huge_table
DROP INDEX col4_idx,
DROP INDEX col1_idx,
DROP INDEX col3_idx,
DROP INDEX col6_idx,
DROP INDEX col2_idx,
DROP INDEX col8_idx,
ADD INDEX new_idx(col3, col6, col1);
This didn't work, either (it still seemed to be running in online mode). I decided to break up the process into two: drop the unnecessary columns, first, then add the new column:
ALTER TABLE huge_table
DROP INDEX col4_idx,
DROP INDEX col1_idx,
DROP INDEX col3_idx,
DROP INDEX col6_idx,
DROP INDEX col2_idx,
DROP INDEX col8_idx;
ALTER TABLE huge_table
ADD INDEX new_idx(col3, col6, col1);
The dropping of the columns only took a few seconds, and the new index completed without errors (again, in online mode). Unfortunately, the new index didn't take.
I presume the entire DDL statement was ultimately rolled back after failing to create the new index, but don't see any record of that; it seems to fail silently. I'm wondering if it's because there's a VIRTUAL column, but I would expect the engine to return some sort of error message. Has anyone else seen this type of issue?
So, right before submitting this question, I found the error:
Error Code: 1799. Creating index 'new_idx' required more than 'innodb_online_alter_log_max_size' bytes of modification log. Please try again.
The issue is that the table is heavily used, and the innodb_online_alter_log_max_size setting is a hard limit to the amount of DML statements that are stored during the online DDL process. As a result, it seems I cannot modify this table in online mode, unless I bump up this value.
https://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_online_alter_log_max_size

How to optimize a MySQL select with rows that do not have matching values in the other table

This question is more or less the same as this one: MySQL select rows that do not have matching column in other table; however, the solution there is not not practical for large data sets.
This table has ~120,000 rows.
CREATE TABLE `tblTimers` (
`TimerID` int(11) NOT NULL,
`TaskID` int(11) NOT NULL,
`UserID` int(11) NOT NULL,
`StartDateTime` datetime NOT NULL,
`dtStopTime` datetime NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
ALTER TABLE `tblTimers`
ADD PRIMARY KEY (`TimerID`);
ALTER TABLE `tblTimers`
MODIFY `TimerID` int(11) NOT NULL AUTO_INCREMENT;
This table has about ~70,000 rows.
CREATE TABLE `tblWorkDays` (
`WorkDayID` int(11) NOT NULL,
`TaskID` int(11) NOT NULL,
`UserID` int(11) NOT NULL,
`WorkDayDate` date NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
ALTER TABLE `tblWorkDays`
ADD PRIMARY KEY (`WorkDayID`);
ALTER TABLE `tblWorkDays`
MODIFY `WorkDayID` int(11) NOT NULL AUTO_INCREMENT;
tblWorkDays should have one line per TaskID per UserID per WorkDayDate, but due to a bug, a few work days are missing despite there being timers for those days; so, I am trying to create a report that shows any timer that does not have a work day associated with it.
SELECT A.TimerID FROM tblTimers A
LEFT JOIN tblWorkDays B ON A.TaskID = B.TaskID AND A.UserID = B.UserID AND DATE(A.StartDateTime) = B.WorkDayDate
WHERE B.WorkDayID IS NULL
Doing this causes the server to time out; so, I am looking for if there is a way to do this more efficiently?
You don't have any indexes on the columns you're joining on, so it has to do full scans of both tables. Try adding the following:
ALTER TABLE tblTimers ADD INDEX (TaskID, UserID);
ALTER TABLE tblWorkDays ADD INDEX (TaskID, UserID);

I need to create a MyIsam table that has an iniital autoincrement value of 0

I am using mysql 5.7.18.
I tried:
CREATE TABLE t1 (
`UID` int(10) auto_increment,
day int(10),
PRIMARY KEY (`UID`)
) ENGINE=MyISAM AUTO_INCREMENT=0;
When i looked at the table info in workbench, it shows the autoinc value is 1.
The weird thing is that I managed to set it to 0 in two other tables!
I also tried:
alter table t1 auto_increment=0;
I did this before any data was written into the table.
I'm at a bit of a loss.
Any recommendations?
Update:
CREATE TABLE `sname`.`z` (
`Uid` INT NOT NULL DEFAULT -1,
PRIMARY KEY (`Uid`))
ENGINE = MyISAM
AUTO_INCREMENT = 0;
This creates the table with the autoincrement start at 0
I do not understand why the top example does not* do what I want it to and the bottom one does.
After even more research, it would appear that you cannot have 0 for an initial value in an autoincrement field.

Best way to add column to a big table with longblob

I have the following table:
CREATE TABLE IF NOT EXISTS PDF_STORAGE (
ID_PDF_STORAGE bigint(20) NOT NULL AUTO_INCREMENT,
DESC_FILE varchar(255) DEFAULT NULL,
PDF_FILE longblob,
LINK_FILE varchar(255) DEFAULT NULL,
VERSION int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (ID_PDF_STORAGE)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
Where PDF_FILE is a file of 10 MB on average. Today it has about 50.000 rows. I need to add a new column to this table but it is taking a long time, more than 10 min, some times giving a 401 error in PhpMyAdmin, so I'd like to know what is the proper way to achieve this...
I already tried:
ALTER TABLE PDF_STORAGE ADD VERSION INT NOT NULL DEFAULT '0' AFTER LINK_FILE ;
and
SET FOREIGN_KEY_CHECKS=0;
CREATE TABLE PDF_STORAGE_new LIKE PDF_STORAGE;
ALTER TABLE PDF_STORAGE_new ADD VERSION INT NOT NULL DEFAULT '0' AFTER LINK_FILE ;
INSERT INTO PDF_STORAGE_new (PDF_STORAGE, DESC_FILE, ID_PDF_STORAGE, LINK_FILE) SELECT * FROM PDF_STORAGE;
RENAME TABLE PDF_STORAGE TO PDF_STORAGE_old, PDF_STORAGE_new TO PDF_STORAGE;
DROP TABLE PDF_STORAGE_old;
SET FOREIGN_KEY_CHECKS = 1;
but they are also slow.. is there a better way?
Thanks
What you are doing now ALTER TABLE is the best approach to my knowledge. You can try making this change when there is not much transaction (or) DB operation going on. I mean say, do the changes in idle time.
ALTER TABLE PDF_STORAGE ADD VERSION INT NOT NULL DEFAULT '0' AFTER LINK_FILE ;
You can as well create a new table same as this table schema along with the new column.
Insert all the records from this table to the newly created table.
Rename the new table to the old table name.
delete the old table.