I'm experimenting with improving the performance of a certain table in my company's database. This table has 7.9 6.9 million rows with the format:
mysql> show fields from BroadcastLog;
+---------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| broadcast_id | int(10) unsigned | YES | MUL | NULL | |
| author_id | int(10) unsigned | YES | MUL | NULL | |
| type | int(11) | NO | MUL | NULL | |
| origin | int(11) | YES | MUL | NULL | |
| date_created | datetime | NO | MUL | NULL | |
| date_modified | datetime | NO | | NULL | |
| old_status | int(10) unsigned | YES | MUL | NULL | |
| new_status | int(10) unsigned | YES | MUL | NULL | |
| json_data | text | YES | | NULL | |
| log_text | text | YES | | NULL | |
+---------------+------------------+------+-----+---------+----------------+
11 rows in set (0.01 sec)
One of the first places I wanted to look to improve this was changing the two text
fields to varchar
fields which I know to generally be more efficient. So I gave it a try:
mysql> alter table BroadcastLog modify log_text varchar(2048);
Query OK, 0 rows affected, 1 warning (1 min 13.08 sec)
Records: 0 Duplicates: 0 Warnings: 1
mysql> show warnings;
+-------+------+---------------------------------------------------+
| Level | Code | Message |
+-------+------+---------------------------------------------------+
| Note | 1246 | Converting column 'log_text' from VARCHAR to TEXT |
+-------+------+---------------------------------------------------+
1 row in set (0.01 sec)
It didn't convert!
I tried to get clever. Let's create a new (temporary) column, copy the data, drop the old column, then rename the new one:
mysql> alter table BroadcastLog add column log_text_vc varchar(2048);
Query OK, 0 rows affected, 1 warning (1 min 13.08 sec)
Records: 0 Duplicates: 0 Warnings: 1
mysql> show warnings;
+-------+------+---------------------------------------------------+
| Level | Code | Message |
+-------+------+---------------------------------------------------+
| Note | 1246 | Converting column 'log_text' from VARCHAR to TEXT |
+-------+------+---------------------------------------------------+
1 row in set (0.01 sec)
Couldn't even create a new column!
I tried to get clever-er. Create a new table, copy the data, drop the old columns, copy the data back:
mysql> create table tmp (id INT UNSIGNED PRIMARY KEY, json_data VARCHAR(1024), log_text VARCHAR(2048));
Query OK, 0 rows affected (0.04 sec)
mysql> insert into tmp (id, json_data, log_text) select id, json_data, log_text from BroadcastLog;
Query OK, 6939076 rows affected (5 min 28.12 sec)
Records: 6939076 Duplicates: 0 Warnings: 0
mysql> alter table BroadcastLog drop column json_data;
Query OK, 0 rows affected (1 min 12.36 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> alter table BroadcastLog drop column log_text;
Query OK, 0 rows affected (1 min 9.10 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> alter table BroadcastLog add column json_data varchar(1024);
Query OK, 0 rows affected (1 min 11.52 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> alter table BroadcastLog add column log_text varchar(2048);
Query OK, 0 rows affected (1 min 15.41 sec)
Records: 0 Duplicates: 0 Warnings: 1
mysql> show warnings;
+-------+------+---------------------------------------------------+
| Level | Code | Message |
+-------+------+---------------------------------------------------+
| Note | 1246 | Converting column 'log_text' from VARCHAR to TEXT |
+-------+------+---------------------------------------------------+
1 row in set (0.01 sec)
mysql> show fields from BroadcastLog;
+---------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| broadcast_id | int(10) unsigned | YES | MUL | NULL | |
| author_id | int(10) unsigned | YES | MUL | NULL | |
| type | int(11) | NO | MUL | NULL | |
| origin | int(11) | YES | MUL | NULL | |
| date_created | datetime | NO | MUL | NULL | |
| date_modified | datetime | NO | | NULL | |
| old_status | int(10) unsigned | YES | MUL | NULL | |
| new_status | int(10) unsigned | YES | MUL | NULL | |
| json_data | varchar(1024) | YES | | NULL | |
| log_text | mediumtext | YES | | NULL | |
+---------------+------------------+------+-----+---------+----------------+
11 rows in set (0.01 sec)
So one field was created properly, but the other was still converted to TEXT, despite the field being completely empty with no data in it
I've been Googling around to try and find an answer, but thus far I've turned up nothing.
Create Table Statement
Per the comments, here's the create table statement (after my above changes, so the log_text
column and json_data
column on my local database may not match the original data I pulled from our production database this morning):
mysql> show create table BroadcastLog\G
*************************** 1. row ***************************
Table: BroadcastLog
Create Table: CREATE TABLE `BroadcastLog` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`broadcast_id` int(10) unsigned DEFAULT NULL,
`author_id` int(10) unsigned DEFAULT NULL,
`type` int(11) NOT NULL,
`origin` int(11) DEFAULT NULL,
`date_created` datetime NOT NULL,
`date_modified` datetime NOT NULL,
`old_status` int(10) unsigned DEFAULT NULL,
`new_status` int(10) unsigned DEFAULT NULL,
`log_text` mediumtext,
PRIMARY KEY (`id`),
KEY `old_status` (`old_status`),
KEY `new_status` (`new_status`),
KEY `broadcast_id` (`broadcast_id`),
KEY `author_id` (`author_id`),
KEY `log_type_and_origin` (`type`,`origin`),
KEY `log_origin` (`origin`),
KEY `bl_date_created` (`date_created`),
CONSTRAINT `fk_BroadcastLog_author_id` FOREIGN KEY (`author_id`) REFERENCES `User` (`id`) ON DELETE SET NULL ON UPDATE CASCADE,
CONSTRAINT `fk_BroadcastLog_broadcast_id` FOREIGN KEY (`broadcast_id`) REFERENCES `Broadcast` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=6941898 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
MySQL Version
mysql> select version();
+-----------+
| version() |
+-----------+
| 5.7.31 |
+-----------+
1 row in set (0.01 sec)
Updated
I updated MySQL and got the same results:
mysql> select version();
+-----------+
| version() |
+-----------+
| 8.0.23 |
+-----------+
1 row in set (0.00 sec)
mysql> show fields from BroadcastLog;
+---------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+--------------+------+-----+---------+----------------+
| id | int unsigned | NO | PRI | NULL | auto_increment |
| broadcast_id | int unsigned | YES | MUL | NULL | |
| author_id | int unsigned | YES | MUL | NULL | |
| type | int | NO | MUL | NULL | |
| origin | int | YES | MUL | NULL | |
| date_created | datetime | NO | | NULL | |
| date_modified | datetime | NO | | NULL | |
| log_text | text | NO | | NULL | |
| json_data | text | YES | | NULL | |
| old_status | int unsigned | YES | MUL | NULL | |
| new_status | int unsigned | YES | MUL | NULL | |
+---------------+--------------+------+-----+---------+----------------+
11 rows in set (0.00 sec)
mysql> alter table BroadcastLog modify log_text varchar(2048);
Query OK, 6939076 rows affected, 1 warning (3 min 22.64 sec)
Records: 6939076 Duplicates: 0 Warnings: 1
mysql> show warnings;
+-------+------+---------------------------------------------------+
| Level | Code | Message |
+-------+------+---------------------------------------------------+
| Note | 1246 | Converting column 'log_text' from VARCHAR to TEXT |
+-------+------+---------------------------------------------------+
1 row in set (0.01 sec)
mysql> show fields from BroadcastLog;
+---------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+--------------+------+-----+---------+----------------+
| id | int unsigned | NO | PRI | NULL | auto_increment |
| broadcast_id | int unsigned | YES | MUL | NULL | |
| author_id | int unsigned | YES | MUL | NULL | |
| type | int | NO | MUL | NULL | |
| origin | int | YES | MUL | NULL | |
| date_created | datetime | NO | | NULL | |
| date_modified | datetime | NO | | NULL | |
| log_text | mediumtext | YES | | NULL | |
| json_data | text | YES | | NULL | |
| old_status | int unsigned | YES | MUL | NULL | |
| new_status | int unsigned | YES | MUL | NULL | |
+---------------+--------------+------+-----+---------+----------------+
11 rows in set (0.02 sec)
I will note one difference I saw in the output, which could have an alternate explanation: it now asys "6939076 rows affected" instead of "0 rows affected". Although I spent a couple hours trying to make sense of this behavior so I had already run multiple ALTER TABLE
statements before I even started this SO thread. It's possible you only get rows affected the first time you try to change the column and I just missed it. It's also possible MySQL 8 just uses a different metric for "affected rows" and has different output.
Either way, still not converting to VARCHAR for some reason