How to move columns in a MySQL table? - mysql

Currently I am having the following MySQL table: Employees (empID, empName, department);
I want to change the table to the following: Employees (empID, department, empName);
How can this be done using ALTER statements?
Note: I want to change only column positions.

If empName is a VARCHAR(50) column:
ALTER TABLE Employees MODIFY COLUMN empName VARCHAR(50) AFTER department;
EDIT
Per the comments, you can also do this:
ALTER TABLE Employees CHANGE COLUMN empName empName VARCHAR(50) AFTER department;
Note that the repetition of empName is deliberate. You have to tell MySQL that you want to keep the same column name.
You should be aware that both syntax versions are specific to MySQL. They won't work, for example, in PostgreSQL or many other DBMSs.
Another edit: As pointed out by #Luis Rossi in a comment, you need to completely specify the altered column definition just before the AFTER modifier. The above examples just have VARCHAR(50), but if you need other characteristics (such as NOT NULL or a default value) you need to include those as well. Consult the docs on ALTER TABLE for more info.

Change column position:
ALTER TABLE Employees
CHANGE empName empName VARCHAR(50) NOT NULL AFTER department;
If you need to move it to the first position you have to use term FIRST at the end of ALTER TABLE CHANGE [COLUMN] query:
ALTER TABLE UserOrder
CHANGE order_id order_id INT(11) NOT NULL FIRST;

phpMyAdmin provides a GUI for this within the structure view of a table.
Check to select the column you want to move and click the change action at the bottom of the column list.
You can then change all of the column properties and you'll find the 'move column' function at the far right of the screen.
Of course this is all just building the queries in the perfectly good top answer but GUI fans might appreciate the alternative.
my phpMyAdmin version is 4.1.7

I had to run this for a column introduced in the later stages of a product, on 10+ tables. So wrote this quick untidy script to generate the alter command for all 'relevant' tables.
SET #NeighboringColumn = '<YOUR COLUMN SHOULD COME AFTER THIS COLUMN>';
SELECT CONCAT("ALTER TABLE `",t.TABLE_NAME,"` CHANGE COLUMN `",COLUMN_NAME,"`
`",COLUMN_NAME,"` ", c.DATA_TYPE, CASE WHEN c.CHARACTER_MAXIMUM_LENGTH IS NOT
NULL THEN CONCAT("(", c.CHARACTER_MAXIMUM_LENGTH, ")") ELSE "" END ," AFTER
`",#NeighboringColumn,"`;")
FROM information_schema.COLUMNS c, information_schema.TABLES t
WHERE c.TABLE_SCHEMA = '<YOUR SCHEMA NAME>'
AND c.COLUMN_NAME = '<COLUMN TO MOVE>'
AND c.TABLE_SCHEMA = t.TABLE_SCHEMA
AND c.TABLE_NAME = t.TABLE_NAME
AND t.TABLE_TYPE = 'BASE TABLE'
AND #NeighboringColumn IN (SELECT COLUMN_NAME
FROM information_schema.COLUMNS c2
WHERE c2.TABLE_NAME = t.TABLE_NAME);

For those using TablePlus, you can just mark all tables, right click -> Copy, in the new table -> Paste.

Related

Easiest way to insert a table into another with conflicting auto-increment values?

I have two tables that have a huge list of columns. They are both the same structure, but different data. However, both tables have an index/auto-increment column that might be similar. Is there an easy way to run a command like this:
insert into table1 (select * from table2);
and have the insert ignore the auto-increment column from table 2? To avoid an error if there's a similar-value in the index column of tables 1 and 2? I want to copy everything over, and have new auto-increments for the table 2 data in table 1.
Alternatively, I don't care what the values are of the auto-increment index. If there was a way to merge the two tables and then re-generate unique AI columns that would also work.
I am aware I could get around this by specifying each field individually in both tables and leaving out the auto-increment column. I'm just wondering if there is an easier way to do this? If there isn't, is there an easy way of generating the field list/statement?
Here is the most efficient way I know of right now. Assuming the A.I. index is called "recno"
ALTER TABLE table1 DROP COLUMN recid;
ALTER TABLE table2 DROP COLUMN recid;
insert into table1 (select * from table2);
ALTER TABLE table1 ADD `recid` INT NOT NULL AUTO_INCREMENT [AFTER `column`], ADD PRIMARY KEY (`recid`);
ALTER TABLE table2 ADD `recid` INT NOT NULL AUTO_INCREMENT [AFTER `column`], ADD PRIMARY KEY (`recid`);
There's not really a way to do that. The * in the SELECT list means "all columns" in ordinal position. There's not exception for columns that meet specified criteria.
The same is true for an omitted list of columns that we're inserting into... it's all of the columns.
The most efficient way (in terms of database resources) to accomplish the specified goal is to list the columns, and omit the auto_increment column from the list.
INSERT INTO t (b,c,d,e) SELECT b,c,d,e FROM s ;
We can get a list of columns names for a table from information_schema.columns...
For example, to get a list of the column names in table2:
SELECT c.column_name
FROM information_schema.columns c
WHERE c.table_schema = 'mydatabase'
AND c.table_name = 'table2'
ORDER
BY c.ordinal_position
To exclude the auto_increment column from the list, we can add a WHERE clause, that excludes that column by it's name
WHERE c.column_name NOT IN ('my_autoincrement_column_name')
or we can check for 'auto_increment' occurring the EXTRA column
WHERE c.extra NOT LIKE '%auto_increment%'
To get a column list for each of the two tables, excluding the auto_increment columns, we could do something like this:
SET group_concat_max_len = 16777216 ;
SELECT GROUP_CONCAT( CONCAT('`',c.column_name,'`')
ORDER BY c.ordinal_position
SEPARATOR ','
) AS `-- column_list`
FROM information_schema.columns c
WHERE c.table_schema = 'mydatabase'
AND c.table_name IN ('table1','table2')
AND c.extra NOT LIKE '%auto_increment%'
GROUP
BY c.table_schema
, c.table_name
ORDER
BY c.table_schema
, c.table_name
EDIT
I'd take the column lists, and build a SQL query.
If we're going to make changes to table2 (dropping the auto_increment column as suggested in another answer to this question), and if we don't need to preserve the values in the column, the easiest change would be to just set all of the values in that column to NULL.
No need to modify table1.
We can remove the auto_increment attribute (and the NOT NULL constraint if that's specified) from the column in table2, and set the column to null. Assuming ai is the name of the auto_increment column, and assuming it's declared to be INT UNSIGNED datatype, we can do:
ALTER TABLE `table2` CHANGE `ai` `ai` INT UNSIGNED COMMENT '' ;
UPDATE `table2` SET `ai` = NULL ;
Then we can do an INSERT INTO table1 SELECT * FROM table2
And then add back the auto_increment attribute to the column in table2.
This approach is more expensive (in terms of the database resources) than a single INSERT ... SELECT.
Generating a column list is an extra step, but the resulting operation would be much more efficient. We could generate a list of columns from just the source table, and then replace the column name with literal NULL value...
INSERT INTO t SELECT NULL,b,c,d FROM s;

How to change datatype for many columns at once in MYSQL?

We started with ID as int(11). Then we switched to Hibernate column auto generation and the ID became bigint(20). So, many foreign keys stopped working unless I change the join column definition from bigint(20) to int (11). Is it possible to write a script that changes all int(11) to bigint (20) for all columns?
The query to find all columns to change is as follows:
select table_name, column_name, column_type
from information_schema.columns
where table_schema = 'rentoptimum'
and column_type='int(11)'
order by table_name, ordinal_position;
Can I update data in the information_schema.columns or I should write the alter script for every table?
You can query INFORMATION_SCHEMA to dynamically generate a script with the ALTER TABLE statements needed to modify these column data types.
You need to be very careful to only modify the data type, and not any other attributes of the column (nullability, auto_increment, signed/unsigned, etc).
Here is an example to get you started. This will generate one ALTER TABLE statement per table in a given schema, even if it has multiple columns to modify. It will replace int(11) with bigint(11), which is slightly different than what you asked for, but the (11) doesn't affect the actual data type. You can tweak that if you want.
select concat('alter table `',t.table_schema,'`.`',t.table_name,'`',
group_concat(' modify column `',c.column_name,'` ',replace(c.column_type,'int','bigint'),
if(c.is_nullable='yes',' null',' not null '),c.extra),';') as the_ddl
into outfile '/tmp/so42114820.sql'
from information_schema.columns c
inner join information_schema.tables t on t.table_schema = c.table_schema and t.table_name = c.table_name
where t.table_schema = 'your_schema'
and t.table_type = 'BASE TABLE'
and c.data_type = 'int'
group by t.table_schema,t.table_name;
set #foreign_key_checks = ##foreign_key_checks;
set foreign_key_checks = 0;
\. /tmp/so42114820.sql
set foreign_key_checks = #foreign_key_checks;

"CONCAT"enated column not showing up in database table

I tried concat function to combine two columns, i got the output also but
my question is why i don't see new column being added to the table. Is concatenating is just a temporary result?
SELECT CONCAT(Name,',',Continent)AS new_address FROM Country
If you want to add a column to the table, you need to alter the table:
alter table country add new_address varchar(255);
Then you can set the value using update:
update country
set new_address = concat_ws(' ', name, continent);
I prefer concat_ws() for this type of operation because it does not return NULL if one of the columns is NULL.
Note: The table has the "correct" values after the update. But, subsequent changes to the table might require that you re-run the update or that you use a trigger to maintain consistency.
On best practice is to define a view to do the calculation:
create view v_country as
select c.*, concat_ws(' ', name, continent) as new_address
from country;
When you access the data through the view, the new_address field will always be correct.
Yes this creates a column that only exists in your SELECT query.
It certainly does not alter the underlying table.
If you wanted to add this computation to the underlying table you could add a generated column as of MySQL 5.7.6.
CREATE TABLE Country
(
Name VARCHAR(100) NOT NULL,
Continent VARCHAR(100) NOT NULL
);
INSERT INTO Country
VALUES ('France', 'Europe'),
('Nigeria','Africa');
ALTER TABLE Country
ADD new_address VARCHAR(201) AS (CONCAT(Name,',',Continent));
SELECT *
FROM Country;
Online Demo

Include the table name in a select statement

I need to include the table name in a SELECT statement, together with some columns and the unique identifier of the table.
I don't know if there is possible to take the table name from a select within that table or some kind of unique identifier.
How can I achieve this?
I thank you for your responses but I fixed this in this way (it was too easy actually)
select 'table1' as tableName, col1, col2 from anyTable;
You will need to query the system catalog of the database to find the primary key and all unique constraints of the table, then choose one that best suites your needs. You can expect to find 0, 1, or more such constraints.
For an Oracle database you'd use something like
select
c.constraint_name,
col.column_name
from
dba_constrants c,
dba_cons_columns col
where
c.table_name = 'YOURTABLE'
and c.constraint_type in ('P', 'U')
and c.constraint_name = col.constraint_name
order by
c.constraint_name,
col.position
For MySQL you would query INFORMATION_SCHEMA.TABLE_CONSTRAINTS and INFORMATION_SCHEMA.KEY_COLUMN_USAGE views in a similar manner.
this will give you all the table names from your database, you can tweak it as you see fit
SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE='BASE TABLE'

How do I know whether the value is inserted automatically or needs to be inserted manually?

well I'm using MS SQL Server (mostly working with the Management Studio) and I can't find out how to know what values from a table do I have to insert and what values are created automatically? How do I view that?
I mean ID's are often calculated automatically, but sometimes you need to insert them by hand, so how exactly can I find out/see that?
Since you are already in SSMS (SQL Server Management Studio), run the following in a query window/tab:
USE [DBNameWhereTableResides]
EXEC sp_help 'SchemaName.TableName'
OR
EXEC DBNameWhereTableResides.dbo.sp_help 'SchemaName.TableName'
You will see entries for "Identity" and "RowGuidCol" that will tell you if the ID is auto-generated.
You will also see a section for Constraints, first column shown being "constraint_type", that will show any DEFAULT constraints that will pre-fill a colummn if the value is not specified in the INSERT.
The second section shows all of the column definitions and the ones marked as "Nullable" = "yes" don't need to be in the INSERT but without being an IDENTITY field or having a DEFAULT it won't get a value and will remain NULL if not specified in the INSERT.
You can use this query to find all tables and id columns
SELECT s.name + '.' + t.name AS TableName, c.name AS ColumnName,
IDENT_SEED(s.name + '.' + t.name) AS Seed,
IDENT_INCR(s.name + '.' + t.name) AS Increment
FROM sys.tables t
INNER JOIN sys.schemas s ON t.schema_id = s.schema_id
INNER JOIN sys.columns c ON c.object_id = t.object_id
AND c.is_identity = 1
CREATE TABLE Product
(
id int IDENTITY, 'this will be automatically inserted
product_name VARCHAR(30),
description varchar(200),
price money NOT NULL
)
INSERT Product (product_name) VALUES ('Strawberry', 1200);
Note: I did not specify "id" in my insert.
You don't have to specify "description", the default will be null
Any column mark NOT NULL must be specified e.g. PRICE