I have a column 'seq' in every table of my database that I would like to delete easily.
I have to do this on occasion in MySQL and am hoping this can be automated.
There isn't a simple magical expression to just do this. You need to generate a list of SQL statements and then run them, somehow.
(Most database folks don't routinely drop columns from a database in production; it takes a lot of time during which the tables are inaccessible, and it's destructive. A fat-finger error could really mess you up.)
You might start by using the information_schema in MySQL to discover which of your tables have a seq column in them. This query will return that list of tables for the database you're currently using.
SELECT DISTINCT TABLE_NAME
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND COLUMN_NAME = 'seq'
You could then adapt that query to, for example, create a list of statements like this.
SELECT DISTINCT
CONCAT('UPDATE ',TABLE_NAME, ' SET seq = 0;') AS stmt
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND COLUMN_NAME = 'seq'
This will produce a result set like this:
UPDATE table_a SET seq = 0;
UPDATE table_b SET seq = 0;
UPDATE user SET seq = 0;
Then you could run these statements one by one. These statements will zero out your seq columns.
Edit
You can also do
CONCAT('ALTER TABLE ',TABLE_NAME, ' DROP COLUMN seq;') AS stmt
to get a drop column statement for each table.
But, you might consider creating views of your tables that don't contain the seq columns, and then exporting to PostgreSQL using those views. If your tables are significant in size, this will save you a lot of time.
Related
Please explain me the below example
SELECT GROUP_CONCAT(COLUMN_NAME)
FROM information_schema.`COLUMNS` C
WHERE table_name = 'table_name'
AND COLUMN_NAME =('columns_name') INTO #COLUMNS;
SET #table = 'table_name';
SET #s = CONCAT('SELECT ',#columns,' FROM ', #table);
PREPARE stmt FROM #s;
This pattern is all about creating dynamic (prepared in MySQL parlance) queries based on the names of columns in a particular table. INFORMATION_SCHEMA is a built-in database with read-only tables describing all the tables in all databases on the MySQL server.
The first query in your sequence retrieves a text string in the local variable #COLUMNS with a value like
id,name,value,description
for a table named table_name with those four columns.
The third one retrieves a string in the local variable #s with a value containing a query like
SELECT id,name,value,description FROM table_name
The fourth one, PREPARE, gets ready to do EXECUTE stmt, which runs the query. You can read about PREPARE and EXECUTE here.
The whole sequence of queries in your question does almost exactly the same thing as SELECT * FROM table_name.
There's a defect in your first query. You should add AND TABLE_SCHEMA = DATABASE() to its WHERE clause. Otherwise, you may pick up columns from tables named table_name in multiple databases.
I have 2 versions of a database (say db_dev and db_beta). I've made some changes in the db_dev database - added some tables, and changed a few columns in some existing tables. I need to find out the list of table names in which changes have been made.
I can easily find out the tables I've added by running the following query on the information_schema database:
SELECT table_name
FROM tables
WHERE table_schema = 'db_dev'
AND table_name NOT IN (SELECT table_name
FROM tables
WHERE table_schema = 'db_beta');
How do I get the table_names whose column_names do not match in the two database versions?
There are many ready made tools available which can give you changed schema by comparing two databases. Here are some tools which can serve your purpose :
Red-Gate's MySQL Schema & Data Compare
Maatkit
MySQL Diff
SQL EDT
Red-Gate's MySQL Compare is best tool for this purpose. Its paid though but they provide 14 days free trial version if you want to do something temporary.
Using information_schema, here is how it works.
First, you know that the information_schema.COLUMNS table contains the columns definition. If one column has been changed, or a table does not exist, it will reflect in the information_schema.COLUMNS table.
Difficult part is that you have to compare all columns of your COLUMNS table. So, you have to select TABLE_CATALOG,TABLE_NAME,COLUMN_NAME,ORDINAL_POSITION,COLUMN_DEFAULT, and so on (which is subject to evolution depending on your MySQL version).
The column list is the result of the following query:
SELECT GROUP_CONCAT(column_name)
FROM information_schema.COLUMNS
WHERE table_schema="information_schema"
AND table_name="COLUMNS" AND column_name!='TABLE_SCHEMA';
After that, we just have to SELECT TABLE_NAME, <column_list> and search for columns which appear once (column inexistent in other table), or where columns have two different definitions (columns altered). So we will have two different count in the resulting query to consider the two cases.
We will so use a prepared statement to retrieve the list of column we want, and grouping the result.
The resulting query does all the process for you:
SELECT CONCAT(
"SELECT DISTINCT TABLE_NAME
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA IN('db_dev', 'db_beta')
GROUP BY table_name, COLUMN_NAME
HAVING count(*)=1 OR
COUNT(DISTINCT CONCAT_WS(',', NULL, ",
GROUP_CONCAT(column_name)
,"))=2;")
FROM information_schema.COLUMNS
WHERE table_schema="information_schema"
AND table_name="COLUMNS" AND column_name!='TABLE_SCHEMA'
INTO #sql;
PREPARE stmt FROM #sql;
EXECUTE #sql;
The following solution does not use an sql query like you tried and does not give you a real list of tables, but it shows you all the changes in both databases.
You can do an sql dump of both database structures :
mysqldump -u root -p --no-data dbname > schema.sql
Then you can compare both files, e.g. using the diff linux tool.
I have ran into trouble when copying my MySQL Tables to a new one, excluding the data, using the query:
CREATE TABLE foo SELECT * FROM bar WHERE 1=0.
The tables are copied, the structure and column names are correctly inserted. But there is a problem with the auto_increment fields and the primary key fields as they are not inserted as they were on the original table. (The fields are not PKs and AI anymore) I am using MySQL 5.5 and PMA 3.5.8.2
I hope someone can help me out.
Thank you SO.
You will probably have to run 2 queries.
CREATE TABLE foo LIKE bar;
ALTER TABLE foo AUTO_INCREMENT = (SELECT `AUTO_INCREMENT` FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'DatabaseName' AND TABLE_NAME = 'bar');
You would have to replace DatabaseName with the name of your database. This is untested, but I think it will give you what you are looking for.
So I tried testing the above query and the ALTER TABLE statement seems to fail due to the select. There might be a better way, but the way that worked for me was to set the auto increment value to a variable and then prepare the statement and execute it.
For example you would go ahead and create your table first:
CREATE TABLE foo LIKE bar;
Then set your ALTER TABLE statement into a variable
SET #ai = CONCAT("ALTER TABLE foo AUTO_INCREMENT =", (SELECT AUTO_INCREMENT FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'databasename' AND TABLE_NAME = 'bar'));
Finally, you would prepare and execute the statement.
PREPARE query FROM #ai;
EXECUTE query;
DEALLOCATE PREPARE query;
Other than your columns, the table structure: indexes, primary keys, triggers, etc. are not copied by this kind of statement. You either need to run a bunch of alter table statements to add your structure or you need to create the table with all the surrounding structure first, then load it with your select.
I've found another thread on this question, but I wasn't able to use its solutions, so I thought I'd ask with more clarity and detail.
I have a large MySQL database representing a vBulletin forum. For several years, this forum has had an error generated on each view, each time creating a new table named aagregate_temp_1251634200, aagregate_temp_1251734400, etc etc. There are about 20,000 of these tables in the database, and I wish to delete them all.
I want to issue a command that says the equivalent of DROP TABLE WHERE TABLE_NAME LIKE 'aggregate_temp%';.
Unfortunately this command doesn't work, and the Google results for this problem are full of elaborate stored procedures beyond my understanding and all seemingly tailored to the more complex problems of different posters.
Is it possible to write a simple statement that drops multiple tables based on a name like match?
There's no single statement to do that.
The simplest approach is to generate a set of statements, and execute them individually.
We can write a simple query that will generate the statements for us:
SELECT CONCAT('DROP TABLE `',t.table_schema,'`.`',t.table_name,'`;') AS stmt
FROM information_schema.tables t
WHERE t.table_schema = 'mydatabase'
AND t.table_name LIKE 'aggregate\_temp%' ESCAPE '\\'
ORDER BY t.table_name
The SELECT statement returns a rowset, but each row conveniently contains the exact SQL statement we need to execute to drop a table. (Note that information_schema is a builtin database that contains metadata. We'd need to replace mydatabase with the name of the database we want to drop tables from.
We can save the resultset from this query as a plain text file, remove any heading line, and voila, we've got a script we can execute in our SQL client.
There's no need for an elaborate stored procedure.
A little googling found this:
SELECT 'DROP TABLE "' + TABLE_NAME + '"'
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME LIKE 'prefix%'
This should generate a script.
Source: Drop all tables whose names begin with a certain string
From memory you have to use prepared statements, for example: plenty of samples on stack exchange
I would recommend this example:
SQL: deleting tables with prefix
The SQL from above, this one includes the specific databasename - it builds it for you
SELECT CONCAT( 'DROP TABLE ', GROUP_CONCAT(table_name) , ';' )
AS statement FROM information_schema.tables
WHERE table_schema = 'database_name' AND table_name LIKE 'myprefix_%';
Here is a different way to do it:
MySQL bulk drop table where table like?
This will delete all tables with prefix "mg_"
No need to copy and paste rowsets and in phpadmin copying and pasting is problematic as it will cut off long table names and replace them with '...' ruining set of sql commands.
Also note that '_' is a special character so thats why 'mg_' should be encoded as 'mg\_'
(and FOREIGN_KEY_CHECKS needs to be disabled in order to avoid error messages)
SET FOREIGN_KEY_CHECKS = 0;
SET GROUP_CONCAT_MAX_LEN=32768;
SET #tables = NULL;
SELECT GROUP_CONCAT('`', table_name, '`') INTO #tables
FROM information_schema.tables
WHERE table_schema = (SELECT DATABASE()) and table_name like 'mg\_%';
SELECT IFNULL(#tables,'dummy') INTO #tables;
SET #tables = CONCAT('DROP TABLE IF EXISTS ', #tables);
PREPARE stmt FROM #tables;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET FOREIGN_KEY_CHECKS = 1;
I have a Microsoft stored procedure that queries two MySQL databases using OpenQuery. The two MySQL databases should be have the same schemas, so I can run the same query on both.
However, we will soon alter the MySQL schemas, and add a column to a table. But the two MySQL databases won't happen at the same time, and I don't know the exact date of the releases.
I therefore want to write the query so that if the new column exists, then I use it in my select. If not, then I use a default value.
Is this possible? (That is have a query that handles differences in the table schema?)
(Not to be confused with 'coelesce' where the field definitely exists, but is simply null.)
You can use the following SELECT statement:
SELECT *
FROM information_schema.COLUMNS
WHERE
TABLE_SCHEMA = 'database name' AND TABLE_NAME = 'your table name'
AND COLUMN_NAME = 'the column name you want to check for'
If the above returns a value, your column is there. If not, then run your alternative SELECT statement
Updated statement:
IF EXISTS (SELECT *
FROM OPENQUERY(servername, 'SELECT *
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = ''database name''
AND TABLE_NAME = ''your table name''
AND COLUMN_NAME = ''the column name you want to check for'' ))