|
| 1 | +--- |
| 2 | +title: How to ALTER COLUMN TYPE in MySQL |
| 3 | +updated_at: 2025/03/17 12:00:00 |
| 4 | +--- |
| 5 | + |
| 6 | +_Official documentation: [ALTER TABLE](https://dev.mysql.com/doc/refman/8.0/en/alter-table.html)_ |
| 7 | + |
| 8 | +<HintBlock type="info"> |
| 9 | + |
| 10 | +Changing column type should be conducted with caution. Some organizations have strict approval process and even disallow altering column type at all. You can enforce [approval process](/docs/administration/custom-approval/) or [disallowing altering column type](/docs/sql-review/review-rules/#column.disallow-change-type) via Bytebase. |
| 11 | + |
| 12 | +</HintBlock> |
| 13 | + |
| 14 | +## Basic Syntax for Altering Column Type |
| 15 | + |
| 16 | +MySQL provides two primary methods to change a column's data type: |
| 17 | + |
| 18 | +```sql |
| 19 | +-- Using MODIFY |
| 20 | +ALTER TABLE table_name |
| 21 | +MODIFY COLUMN column_name new_data_type; |
| 22 | + |
| 23 | +-- Using CHANGE (allows renaming the column) |
| 24 | +ALTER TABLE table_name |
| 25 | +CHANGE COLUMN old_column_name new_column_name new_data_type; |
| 26 | +``` |
| 27 | + |
| 28 | +## Simple Type Conversions |
| 29 | + |
| 30 | +For straightforward conversions: |
| 31 | + |
| 32 | +```sql |
| 33 | +-- Change an integer column to bigint |
| 34 | +ALTER TABLE orders |
| 35 | +MODIFY COLUMN order_id BIGINT; |
| 36 | + |
| 37 | +-- Change a varchar column to text |
| 38 | +ALTER TABLE customers |
| 39 | +MODIFY COLUMN notes TEXT; |
| 40 | + |
| 41 | +-- Change a float column to decimal with precision |
| 42 | +ALTER TABLE products |
| 43 | +MODIFY COLUMN price DECIMAL(10,2); |
| 44 | +``` |
| 45 | + |
| 46 | +## String Type Conversions |
| 47 | + |
| 48 | +```sql |
| 49 | +-- VARCHAR to TEXT (no data loss) |
| 50 | +ALTER TABLE messages |
| 51 | +MODIFY COLUMN content TEXT; |
| 52 | + |
| 53 | +-- VARCHAR to CHAR (fixed length) |
| 54 | +ALTER TABLE countries |
| 55 | +MODIFY COLUMN country_code CHAR(2); |
| 56 | + |
| 57 | +-- TEXT to VARCHAR with potential truncation |
| 58 | +ALTER TABLE products |
| 59 | +MODIFY COLUMN description VARCHAR(255); |
| 60 | +``` |
| 61 | + |
| 62 | +## Numeric Type Conversions |
| 63 | + |
| 64 | +```sql |
| 65 | +-- Integer to BIGINT (safe, no data loss) |
| 66 | +ALTER TABLE measurements |
| 67 | +MODIFY COLUMN value BIGINT; |
| 68 | + |
| 69 | +-- DECIMAL to INTEGER (truncation of fractional part) |
| 70 | +ALTER TABLE products |
| 71 | +MODIFY COLUMN price INT; |
| 72 | + |
| 73 | +-- FLOAT to DECIMAL (fixed precision) |
| 74 | +ALTER TABLE financial |
| 75 | +MODIFY COLUMN amount DECIMAL(15,2); |
| 76 | +``` |
| 77 | + |
| 78 | +## Date and Time Conversions |
| 79 | + |
| 80 | +```sql |
| 81 | +-- DATETIME to DATE (drops time portion) |
| 82 | +ALTER TABLE events |
| 83 | +MODIFY COLUMN event_datetime DATE; |
| 84 | + |
| 85 | +-- DATE to DATETIME |
| 86 | +ALTER TABLE appointments |
| 87 | +MODIFY COLUMN appointment_date DATETIME; |
| 88 | + |
| 89 | +-- TIMESTAMP to DATETIME |
| 90 | +ALTER TABLE logs |
| 91 | +MODIFY COLUMN log_time DATETIME; |
| 92 | + |
| 93 | +-- Converting string to date/time |
| 94 | +ALTER TABLE imported_events |
| 95 | +MODIFY COLUMN event_date DATE; |
| 96 | +``` |
| 97 | + |
| 98 | +## ENUM and SET Conversions |
| 99 | + |
| 100 | +```sql |
| 101 | +-- Converting VARCHAR to ENUM |
| 102 | +ALTER TABLE tickets |
| 103 | +MODIFY COLUMN status ENUM('open', 'in_progress', 'closed', 'cancelled'); |
| 104 | + |
| 105 | +-- Expanding an existing ENUM |
| 106 | +ALTER TABLE products |
| 107 | +MODIFY COLUMN size ENUM('small', 'medium', 'large', 'extra-large'); |
| 108 | + |
| 109 | +-- Converting to SET type |
| 110 | +ALTER TABLE users |
| 111 | +MODIFY COLUMN roles SET('admin', 'editor', 'viewer'); |
| 112 | +``` |
| 113 | + |
| 114 | +## JSON Conversions |
| 115 | + |
| 116 | +MySQL 5.7+ supports JSON data type: |
| 117 | + |
| 118 | +```sql |
| 119 | +-- Converting TEXT to JSON |
| 120 | +ALTER TABLE configurations |
| 121 | +MODIFY COLUMN config JSON; |
| 122 | + |
| 123 | +-- Converting VARCHAR to JSON |
| 124 | +ALTER TABLE api_responses |
| 125 | +MODIFY COLUMN response JSON; |
| 126 | +``` |
| 127 | + |
| 128 | +## Handling Special Cases |
| 129 | + |
| 130 | +### Converting with Default Values |
| 131 | + |
| 132 | +```sql |
| 133 | +-- Set default value during conversion |
| 134 | +ALTER TABLE users |
| 135 | +MODIFY COLUMN status VARCHAR(20) DEFAULT 'active'; |
| 136 | + |
| 137 | +-- Update NULL values before conversion to NOT NULL |
| 138 | +UPDATE users SET last_login = '1970-01-01' WHERE last_login IS NULL; |
| 139 | +ALTER TABLE users |
| 140 | +MODIFY COLUMN last_login DATETIME NOT NULL; |
| 141 | +``` |
| 142 | + |
| 143 | +### Preserving Column Attributes |
| 144 | + |
| 145 | +When modifying a column's type, you need to specify all attributes you want to keep: |
| 146 | + |
| 147 | +```sql |
| 148 | +-- Before: INT NOT NULL AUTO_INCREMENT |
| 149 | +-- After: BIGINT NOT NULL AUTO_INCREMENT |
| 150 | +ALTER TABLE customers |
| 151 | +MODIFY COLUMN id BIGINT NOT NULL AUTO_INCREMENT; |
| 152 | +``` |
| 153 | + |
| 154 | +### Converting with Length Constraints |
| 155 | + |
| 156 | +```sql |
| 157 | +-- Check potential data truncation before conversion |
| 158 | +SELECT COUNT(*) FROM products WHERE LENGTH(description) > 100; |
| 159 | + |
| 160 | +-- Perform the conversion (potential data loss) |
| 161 | +ALTER TABLE products |
| 162 | +MODIFY COLUMN description VARCHAR(100); |
| 163 | +``` |
| 164 | + |
| 165 | +## Performance Considerations |
| 166 | + |
| 167 | +### Table Locking |
| 168 | + |
| 169 | +```sql |
| 170 | +-- Specify algorithm and lock for better performance |
| 171 | +ALTER TABLE large_table |
| 172 | +MODIFY COLUMN data TEXT, |
| 173 | +ALGORITHM=INPLACE, LOCK=NONE; |
| 174 | + |
| 175 | +-- Not all modifications support INPLACE algorithm |
| 176 | +-- Check if your modification is supported: |
| 177 | +-- https://dev.mysql.com/doc/refman/8.0/en/innodb-online-ddl-operations.html |
| 178 | +``` |
| 179 | + |
| 180 | +### Low-Impact Approaches for Production |
| 181 | + |
| 182 | +For large tables in production, a multi-step approach might be preferred: |
| 183 | + |
| 184 | +```sql |
| 185 | +-- 1. Add a new column |
| 186 | +ALTER TABLE large_table ADD COLUMN new_column new_type; |
| 187 | + |
| 188 | +-- 2. Update data in the new column |
| 189 | +UPDATE large_table SET new_column = old_column; |
| 190 | + |
| 191 | +-- 3. (Optional) Add any constraints |
| 192 | +ALTER TABLE large_table MODIFY COLUMN new_column new_type NOT NULL; |
| 193 | + |
| 194 | +-- 4. Drop the old column when ready |
| 195 | +ALTER TABLE large_table DROP COLUMN old_column; |
| 196 | + |
| 197 | +-- 5. Rename the new column to the old name |
| 198 | +ALTER TABLE large_table CHANGE COLUMN new_column old_column new_type; |
| 199 | +``` |
| 200 | + |
| 201 | +## Common Errors and Solutions |
| 202 | + |
| 203 | +See [MySQL Error Reference](/reference/mysql/error/overview/) for errors you may encounter. |
| 204 | + |
| 205 | +Here are the most common errors when altering column types: |
| 206 | + |
| 207 | +### Error 1265: Data truncated |
| 208 | + |
| 209 | +```sql |
| 210 | +-- This happens when data doesn't fit the new type |
| 211 | +-- Identify problematic rows |
| 212 | +SELECT * FROM table_name |
| 213 | +WHERE LENGTH(column_name) > new_length; |
| 214 | + |
| 215 | +-- Fix the data first, then alter the column |
| 216 | +UPDATE table_name |
| 217 | +SET column_name = SUBSTRING(column_name, 1, new_length) |
| 218 | +WHERE LENGTH(column_name) > new_length; |
| 219 | +``` |
| 220 | + |
| 221 | +### Error 1366: Incorrect integer value |
| 222 | + |
| 223 | +```sql |
| 224 | +-- When converting string to numeric and non-numeric values exist |
| 225 | +-- Find problematic rows |
| 226 | +SELECT * FROM table_name |
| 227 | +WHERE column_name REGEXP '[^0-9]'; |
| 228 | + |
| 229 | +-- Clean data before conversion |
| 230 | +UPDATE table_name SET column_name = 0 |
| 231 | +WHERE column_name REGEXP '[^0-9]'; |
| 232 | +``` |
| 233 | + |
| 234 | +### Error 1292: Incorrect datetime value |
| 235 | + |
| 236 | +```sql |
| 237 | +-- When converting to DATE/DATETIME with invalid format |
| 238 | +-- Find invalid dates |
| 239 | +SELECT * FROM table_name |
| 240 | +WHERE STR_TO_DATE(column_name, '%Y-%m-%d') IS NULL; |
| 241 | + |
| 242 | +-- Fix dates before conversion |
| 243 | +UPDATE table_name |
| 244 | +SET column_name = '2000-01-01' |
| 245 | +WHERE STR_TO_DATE(column_name, '%Y-%m-%d') IS NULL; |
| 246 | +``` |
| 247 | + |
| 248 | +### Error 1118: Row size too large |
| 249 | + |
| 250 | +```sql |
| 251 | +-- Happens when the new column size would make row exceed max row size |
| 252 | +-- Consider using vertical partitioning or TEXT/BLOB columns |
| 253 | +ALTER TABLE large_table |
| 254 | +MODIFY COLUMN large_column TEXT; |
| 255 | +``` |
| 256 | + |
| 257 | +## Data Type Conversion Compatibility |
| 258 | + |
| 259 | +Not all conversions preserve data integrity. Here's a quick reference: |
| 260 | + |
| 261 | +| From Type | To Type | Data Safety | Notes | |
| 262 | +| --------- | ------- | ----------- | ----------------------------------------- | |
| 263 | +| INT | BIGINT | Safe | No data loss | |
| 264 | +| BIGINT | INT | Unsafe | Potential overflow | |
| 265 | +| VARCHAR | TEXT | Safe | No data loss | |
| 266 | +| TEXT | VARCHAR | Unsafe | Potential truncation | |
| 267 | +| FLOAT | DECIMAL | Mostly Safe | Potential precision issues | |
| 268 | +| DATETIME | DATE | Unsafe | Time portion lost | |
| 269 | +| CHAR | VARCHAR | Safe | No data loss | |
| 270 | +| VARCHAR | ENUM | Unsafe | Non-matching values become empty or error | |
| 271 | + |
| 272 | +## Best Practices |
| 273 | + |
| 274 | +1. **Backup First**: Always back up your database before altering column types |
| 275 | + |
| 276 | +2. **Test in Development**: Never make type changes directly in production first |
| 277 | + |
| 278 | +3. **Check Data**: Verify data compatibility before conversion |
| 279 | + |
| 280 | +4. **Transaction Safety**: Consider using transactions for related changes |
| 281 | + |
| 282 | +5. **Choose Optimal Timing**: Schedule alterations during low-traffic periods |
| 283 | + |
| 284 | +6. **Use ALGORITHM and LOCK**: Specify optimal algorithm when possible |
| 285 | + |
| 286 | +7. **Consider Index Impact**: Be aware that indexes may need to be rebuilt |
0 commit comments