Build column definition from INFORMATION_SCHEMA COLUMNS table - mysql

I want to rename columns in database programmatically and MySQL enforces me to specify column definition in CHANGE statement.
ALTER TABLE table_name CHANGE old_name new_name column_definition
So, I want to build column definition from INFORMATION_SCHEMA.COLUMNS table. I've read answers which adjust me to parse output of SHOW CREATE TABLE table_name, but I do not want to do that in some reasons.
I want to get column definition as result of single SQL script if it is possible.

Try this:
select COLUMN_TYPE from INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = '{database name}' AND TABLE_NAME = '{table name}' AND COLUMN_NAME = '{column name}';
You'll need to fill in the right values, but it should give you the precise string you'll need to fill into the ALTER statement. You will need to do some additional work to deal with default statements and NULL settings.
The complete sample:
SELECT CONCAT(
CAST(COLUMN_NAME AS CHAR),
' ',
CAST(COLUMN_TYPE AS CHAR),
IF(ISNULL(CHARACTER_SET_NAME),
'',
CONCAT(' CHARACTER SET ', CHARACTER_SET_NAME)),
IF(ISNULL(COLLATION_NAME), '', CONCAT(' COLLATE ', COLLATION_NAME)),
' ',
IF(IS_NULLABLE = 'NO', 'NOT NULL ', ''),
IF(IS_NULLABLE = 'NO' AND ISNULL(COLUMN_DEFAULT),
'',
CONCAT('DEFAULT ', QUOTE(COLUMN_DEFAULT), ' ')),
UPPER(extra))
AS column_definition
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'table_schema'
AND TABLE_NAME = 'table_name'
AND COLUMN_NAME = 'column_name';
EDIT Added quoting of default value, collation and charset.

Related

Replace space with underscore in all table names in database

I'm trying to replace all space (' ') with a underscore in all the table names in a database using PhpMyAdmin.
Ex:
Table 1 --> Table_1
I was wondering if this is possible. I know it's possible to do this for columns but I was wondering if someone could write me something for tables. I don't use PhpMyAdmin very often, but I installed it in this case becuase it works easily.
I'm not sure if you could do this in a stored procedure, but it is easy enough to have a query generate a script for you:
SELECT CONCAT('RENAME TABLE `'
, table_name
, '` TO `'
, REPLACE(table_name, ' ', '_')
, '`;'
) AS renameQuery
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = 'mySchema' AND TABLE_NAME LIKE '% %'
;
You could run a query like this to generate the SQL that will perform a rename:
SELECT CONCAT('RENAME TABLE ', table_name, ' TO ' , REPLACE(table_name, ' ', '_') , ';')
FROM information_schema.tables
WHERE table_schema = 'your_schema_name';
Remember to substitute your_schema_name for whatever your database is called.
To run the query in phpMyAdmin you can click the SQL tab at the top of the window, then paste the SQL into the box. The result of the above query will be SQL generated based off of the existing table names. Just copy this resulting SQL back into the textbox and run it again to perform the renames.
You can also rename all table with one command:
SELECT
CONCAT('RENAME TABLE '
,GROUP_CONCAT(
CONCAT('`',TABLE_NAME,'` TO ', REPLACE(TABLE_NAME,' ','_'))
SEPARATOR ', '),';') AS query
FROM information_schema.Tables
WHERE TABLE_SCHEMA = 'yourschema'
AND TABLE_NAME LIKE '% %';
sample
MariaDB [yourschema]> SELECT CONCAT('RENAME TABLE ' ,GROUP_CONCAT( CONCAT('`',TABLE_NAME,'` TO ', REPLACE(TABLE_NAME,' ','_')) SEPARATOR ', '),';') AS query FROM information_schema.Tables WHERE TABLE_SCHEMA = 'yourschema' AND TABLE_NAME LIKE '% %';
+--------------------------------------------------------------------------------------+
| query |
+--------------------------------------------------------------------------------------+
| RENAME TABLE `esercizio 1` TO esercizio_1, `my_table 1` TO my_table_1, `t 1` TO t_1; |
+--------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
MariaDB [yourschema]>

Change the Datatype of a Column in the entire Database - MySQL

I am new to MySQL. I have designed few tables with Char datatype. Now I want to change all the char datatype columns to Varchar with respective size mentioned in Char.
For example.
Existing - Phone Char(10)
Expected - Phone Varchar(10)
There are around 50 Plus tables. I know how to alter the datatype for each and every table and column but is there any better way to change the datatype in a single shot.
If you can't write a script to do the job, write a script that writes a script to do the job!
You want a bunch of ALTER TABLE statements:
SET SESSION group_concat_max_len = 1000000; -- Bumps the limit of 1028
SELECT GROUP_CONCAT(
CONCAT(
'ALTER TABLE `',
table_name,
'` MODIFY COLUMN `',
column_name,
'` VARCHAR(',
character_maximum_length,
')'
)
SEPARATOR ';\n') your_alter_statements
FROM information_schema.columns
WHERE table_schema = 'concrete'
AND data_type = 'char';
This'll result in:
ALTER TABLE `table1` MODIFY COLUMN `col1` VARCHAR(10);
ALTER TABLE `table1` MODIFY COLUMN `col2` VARCHAR(10);
ALTER TABLE `table2` MODIFY COLUMN `col1` VARCHAR(10);
ALTER TABLE `table3` MODIFY COLUMN `col1` VARCHAR(10);
ALTER TABLE `table3` MODIFY COLUMN `col2` VARCHAR(10);
Run that and you can go home early!
UPDATE: Stopped the truncation by adding group_concat_max_len. Made the length dynamic based on the columns length.
MySQL only one liner solution:
SELECT 'ALTER TABLE ',TABLE_NAME, ' MODIFY COLUMN ',COLUMN_NAME, CONCAT(' VARCHAR(', CHARACTER_MAXIMUM_LENGTH, ');')
FROM INFORMATION_SCHEMA.COLUMNS where DATA_TYPE = 'char' AND TABLE_SCHEMA='concrete'
Above query will return all ALTER TABLE statements, example shown below:
ALTER TABLE tablename1 MODIFY COLUMN col_name1 VARCHAR(17);
ALTER TABLE tablename2 MODIFY COLUMN col_name2 VARCHAR(17);
ALTER TABLE tablename3 MODIFY COLUMN col_name3 VARCHAR(60);
Copy the resulting rows and execute, so you can go home even more early.
Additionally, below is PHP/MySQL solution:
// Initialise Connection
define('DB_HOST', 'HOST_NAME_HERE');
define('DB_NAME', 'DB_NAME_HERE');
define('DB_USER_NAME', 'DB_USER_NAME_HERE');
define('DB_PASSWORD', 'DB_PASSWORD_HERE');
$pdo = new PDO('mysql:host=' . DB_HOST . ';dbname=' . DB_NAME, DB_USER_NAME, DB_PASSWORD, [
PDO::ATTR_PERSISTENT => false,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"
]);
// Get a list of CHAR columns
$sql = "SELECT TABLE_NAME, COLUMN_NAME, CHARACTER_MAXIMUM_LENGTH, IS_NULLABLE, COLUMN_DEFAULT
FROM INFORMATION_SCHEMA.COLUMNS where DATA_TYPE = 'char' AND TABLE_SCHEMA='" . DB_NAME . "'";
$stmt = $pdo->prepare($sql);
$stmt->execute();
$data = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Change CHAR to VARCHAR
foreach ($data as $rec) {
$ddq = 'ALTER TABLE ' . $rec['TABLE_NAME'] . ' CHANGE ' . $rec['COLUMN_NAME'] . ' ' . $rec['COLUMN_NAME'] . ' VARCHAR(' .
$rec['CHARACTER_MAXIMUM_LENGTH'] . ') ' . ($rec['IS_NULLABLE'] == 'YES' ? 'NULL' : 'NOT NULL') . (isset($rec['COLUMN_DEFAULT']) ? " DEFAULT '{$rec['COLUMN_DEFAULT']}'" : '');
$pdo->query($ddq);
}
After analyzing the web, I got the answer and changed the datatype using this query.
select
concat('ALTER TABLE ', table_name,' MODIFY COLUMN ', column_name,' ', REPLACE (column_type, 'char', 'varchar'),';')
from information_schema.columns
where
data_type='char'
and table_schema='DBName';
If your field is PK ('id' smallint unsigned for example) and some other tables have constraints with foreign keys for this PK use this query.
note: for modern mysql servers remove ONLY_FULL_GROUP_BY from Variables -> sql mode, by default it contains:
ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
Then insert and execute this query:
SET group_concat_max_len = 10000;
SET #database_name = "database1";
SET #table_name = "table1";
SET #change = "mediumint unsigned";
SELECT DISTINCT
`table_name`,
`column_name`,
`constraint_name`,
`referenced_table_name`,
`referenced_column_name`,
CONCAT(
GROUP_CONCAT('ALTER TABLE `',table_name,'` DROP FOREIGN KEY `',constraint_name, '`' SEPARATOR ';'),
';',
GROUP_CONCAT('ALTER TABLE `',table_name,'` CHANGE `',column_name,'` `',column_name,'` ',#change SEPARATOR ';'),
';',
CONCAT('ALTER TABLE `',#table_name,'` CHANGE `',referenced_column_name,'` `',referenced_column_name,'` ',#change, ' NOT NULL AUTO_INCREMENT'),
';',
GROUP_CONCAT('ALTER TABLE `',table_name,'` ADD CONSTRAINT `',constraint_name,'` FOREIGN KEY(',column_name,') REFERENCES ',referenced_table_name,'(',referenced_column_name,')' SEPARATOR ';'),
';'
) AS query
FROM `information_schema`.`key_column_usage`
WHERE `referenced_table_name` IS NOT NULL
AND `referenced_column_name` IS NOT NULL
AND `constraint_schema` = #database_name
AND `referenced_table_name` = #table_name
GROUP BY `referenced_table_name`;
note: check EXTRA OPTIONS to show full text for fields in result.
Then copy and execute all generated queries.
Restore ONLY_FULL_GROUP_BY in Variables.
Enjoy!

Making All Column Name Lower Case MySQL

I have inherited a large MySQL database.
Its a mess: half the column names are upper case, other mixed case, other lower case.
I wish to standardize them
Is there a query I can run to alter each one so that they are all lower case?
Try this for rename all tables and columns to lower case:-
SELECT CONCAT('ALTER TABLE ', TABLE_NAME, ' CHANGE `', COLUMN_NAME, '` `',
LOWER(COLUMN_NAME), '` ', COLUMN_TYPE, ';')
FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = '{your schema name}'
Yes. You will be able to compose a query that will
1. Enumerate the tables in the database.
2. Enumerate the columns for each database.
3. If the column name is not equal to LCASE of its name, rename it to LCASE of its name.
When you post an example of what you've tried, I may be able to update this answer to be more helpful.
By this
SELECT CONCAT(
'ALTER TABLE ', table_name, ' CHANGE ', column_name, ' ',
LOWER(column_name), ' ', column_type, ' ', extra,
CASE WHEN IS_NULLABLE = 'YES' THEN ' NULL' ELSE ' NOT NULL' END, ';') AS line
FROM information_schema.columns
WHERE table_schema = '<DBNAME>'
AND data_type IN ('char', 'varchar','INT', 'TINYINT', 'datetime','text','double','decimal')
ORDER BY line;
its not quite perfect but close enough.

Alter all columns WHERE datatype

Is there a way to change all columns that have a specific datatype in a database?
I have some columns that their datatype is datetime(6) and if any is like that I would just want them to be datatime ... without the 6 fractional numbers. Is this possible to do in MySql without having to specify every column? Thanks
You must create text string with necessary statements and save it in somewhere (i don't have columns with datetime(6), but must be like that):
select
concat('ALTER TABLE ',
TABLE_SCHEMA,
'.',
table_name,
' CHANGE COLUMN ',
Column_name,
' ',
Column_name,
' DATETIME NULL DEFAULT NULL')
from
INFORMATION_SCHEMA.COLUMNS
where
table_schema = 'my_schema'
and data_type = 'datetime'
and character_maximum_length = 6 #may be numeric_precision, i don't know
after use exec statement, like this:
PREPARE stmt1 FROM 'ALTER TABLE ....';
EXECUTE stmt1

Modify all columns in a table to 'not null' no matter what

Is there a simple command to modify all columns in a table to not null?
Because now I have to find the name and the type of each column to write something like this:
ALTER TABLE testing CHANGE testing_text testing_text VARCHAR(10) NOT NULL;
...
and that is a lot of work for me.
A quick way is to write your alter statements into a file
select
concat('ALTER TABLE ', c.TABLE_NAME, ' CHANGE ', c.COLUMN_NAME, ' ', c.COLUMN_NAME, ' ', c.COLUMN_TYPE, ' NOT NULL;') as alter_statement
into outfile '/tmp/alter.txt'
from information_schema.COLUMNS c
where
c.IS_NULLABLE = 'YES'
and c.TABLE_SCHEMA = 'your_database_name';
Then execute the file's content
source /tmp/alter.txt
and you're done...
Tested it in a playground DB and it worked for me, still you might want to double check the file before executing :)
P.S.: I haven't checked how NULL values are handled. IIRC you have to have a default value? Not sure right now. Please test this before using it.
EDIT 1: To have one statement per table:
select
concat('ALTER TABLE ', c.TABLE_NAME, GROUP_CONCAT(' MODIFY COLUMN ', c.COLUMN_NAME, ' ', c.COLUMN_TYPE, ' NOT NULL')) as alter_statement
from information_schema.COLUMNS c
where
c.IS_NULLABLE = 'YES'
and c.TABLE_SCHEMA = 'your_database_name'
group by c.TABLE_NAME
EDIT 2:
This one works
select concat(alter_statement, ';')
into outfile '/tmp/alter.txt'
from (
select
concat('ALTER TABLE ', c.TABLE_NAME, GROUP_CONCAT(' CHANGE ', c.COLUMN_NAME, ' ', c.COLUMN_NAME, ' ', c.COLUMN_TYPE, ' NOT NULL')) as alter_statement
from information_schema.COLUMNS c
where
c.IS_NULLABLE = 'YES'
and c.TABLE_SCHEMA = 'playground'
group by c.TABLE_NAME
) sq
, but group_concat() is limited in length, so you might get syntax errors if you have too many columns in a table. Then you still have the first option from above, or you have a look at this manual entry:
The result is truncated to the maximum length that is given by the group_concat_max_len system variable, which has a default value of 1024. The value can be set higher, although the effective maximum length of the return value is constrained by the value of max_allowed_packet. The syntax to change the value of group_concat_max_len at runtime is as follows, where val is an unsigned integer:
SET [GLOBAL | SESSION] group_concat_max_len = val;