Rename all FKs and IDXs names - mysql

I am working on MySql Workbench and everytime I rename a table/column, the existing FKs and IDXs names don't change. I ended up with a model with all the FK/IDX names messed up. Is there any way to regenerate all names according to the new tables and columns names or do I have to change them one by one?

Straight on the model i haven't found any regeneration of FKs etc. A workaround is the following, but you have to forward engineer your model first in order to have a schema:
Use and customize the following statement:
SELECT TABLE_NAME, INDEX_NAME, COLUMN_NAME,
CONCAT('ALTER TABLE `your_schema`.`',TABLE_NAME,'` DROP INDEX `',INDEX_NAME,'`, ADD INDEX `fk_',TABLE_NAME,'_',COLUMN_NAME,'_idx` (`',COLUMN_NAME,'` ASC);') as statement
FROM INFORMATION_SCHEMA.STATISTICS
WHERE TABLE_SCHEMA = 'your_schema';
Customize the WHERE above in order to exclude e.g. PRIMARY indexes or anything else.
You can also set the prefix and suffix of index names that is appropriate for you in the CONCAT expression.
The first 3 columns in the above SELECT will be used in order to create the fourth expression. You can use the result of the fourth expression to batch-create the needed statements.
It may not seems the fastest solution but, once you add your final statement to your snippets, the next time will be quick and easy to regenerate your schema indexes.

Related

ALTER TABLE to append ENUM values from subquery

I've looked around for a while, but I am having trouble finding the answer to this. I want to run a single ALTER TABLE to append values to an ENUM field, without hitting any race conditions. The best way I can think of is something similar to this:
ALTER TABLE 'my_table' MODIFY COLUMN 'my_enum' *results_from_subquery_here*
where the subquery is the following:
(SELECT column_type, concat(TRIM(TRAILING ')' FROM column_type),",'new_enum_value')")
FROM information_schema.columns
as T
WHERE table_name = 'my_table'
and column_name ='my_enum')
Which clearly can't just be appended to the first like that.
I've seen certain approaches that use PREPARE and EXECUTE, or trivially via dbi (in perl), but I want to know if it is possible to do without them. Ie, I want to know if it can be done in a single statement, and avoid race conditions.
Also, I know that ENUMs are "evil", in case you were about to mention that.
No, this operation cannot be performed in a single statement.
The ALTER TABLE statement doesn't have any support for running a SELECT subquery. Which is why you've found what you found: a separate SELECT statement being run, and then a second ALTER TABLE statement being run.
FOLLOWUP
To get this type of operation to be "atomic", you'd need to obtain an exclusive lock on the table. The ALTER TABLE does get an exclusive lock, but I think you're asking about two sessions...
session operation
------- -----------------------
one get enum defn ('a','b') and add 'c'
two get enum defn ('a','b') and add 'fee'
two set enum defn ('a','b','fee')
one set enum defn ('a','b','c')
To prevent one from "overwriting" the other, you'd need to establish some sort of locking mechanism to prevent two sessions from performing this operation concurrently.
(I don't think ENUMs are evil; yes, there are some limitations, and we need to take care in using the ENUM datatype.)

MySQL - Massive structural changes

I'm working with a database that has references in nearly every table to the "user" table to track things like who last created or updated a record. Unfortunately, the user table's primary key is "username", rather than an auto incremented id. We have frequent situations where a username needs to change because of typos or changes in the user profile, and this is really time-consuming under the current design.
I want to change the structure to use an auto-incremented id, but am not finding an easy way to do it. In SqlServer, I could use a combination of introspection on references, cursors, and structural changes to do this without much effort. But from what I've read about MySQL, I (1) can only use loops from WITHIN a stored procedure and (2) can only change structure from OUTSIDE a stored procedure.
The only option I've found is the pretty tedious task of
(1) Creating a temporary table to find references to username:
CREATE TEMPORARY TABLE IF NOT EXISTS username_refs as (select table_name, column_name, constraint_name, referenced_table_name, referenced_column_name from information_schema.key_column_usage where REFERENCED_TABLE_NAME = 'user' and referenced_column_name = 'username');
(2) Building up the set of SQL statements to accomplish each task. For example, this creates a "shadow" column on each table that references username:
select CONCAT('ALTER TABLE `', table_name, '` ADD COLUMN `', column_name,'_2` bigint(20) ;') from username_refs;
This ends up being hundreds of lines of sql. Is there an easier way to accomplish this that I'm just missing?
Some people prefer to write a script in an external language to loop over the tables and format the desired ALTER TABLE statements. For example, whatever you preference is of Perl, Ruby, PHP, Python, etc. Every scripting language supports an API to access MySQL.
What you're doing is fine too. You can use SQL to generate the list of ALTER statements, save the output of that to a file, and then run the file as an SQL script.
By the way, you don't necessarily need a separate auto-increment key. You define your foreign keys to username so as to ON UPDATE CASCADE. See "Referential Actions" in https://dev.mysql.com/doc/refman/5.6/en/create-table-foreign-keys.html

How do I see which columns throughout my database reference one particular column as a foreign key? Like a "reverse" foreign key?

I've got a pretty big relational database and am working on the admin backend. I want to know which tables reference a column in one particular table.
For example: let's say I've got a table products with id as the primary index. I can have a lot of tables that reference this index, such as an orders table, a user_bookmarks table, and a product_reviews table. If I want to delete a particular product, I first need to do some work with the other tables--a simple "cascade" or "delete" directive wouldn't be enough. How do I get MySQL to tell me exactly which columns in which tables are referencing the products.id index?
Bonus question: is there a built-in way to get this info using phpmyadmin?
SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
WHERE (REFERENCED_TABLE_SCHEMA, REFERENCED_TABLE_NAME) = ('mydatabase', 'products')
Re your comment:
MySQL doesn't support recursive queries, so unfortunately there's no way to find the complete graph of foreign keys in a single query. The best you could do is for each of the results of the above query, run the query again, substituting the TABLE_SCHEMA and TABLE_NAME as the string constants in the WHERE clause (be careful not to enter into infinite loops if you have circular foreign keys).
Could you not read the Schema from the database into MySQL Workbench, and then use the tool to plot out the relationships between the tables? I have not tried this myself, as I do the design in MySQL Workbench and then the mappings, etc... and then export to MySQL to create the database.

How can I replace a string in a MySQL database for all tables in all fields in all rows?

I have a Moodle installation that I migrated to another server and I need to change several references to the old domain.
How can I replace a string for another string in MySQL for a given database searching all tables, all fields, and all rows?
I don't need to change the field names, just the values.
Related: How can I use mySQL replace() to replace strings in multiple records?
But the marked as answer solution implies I strongly type the table name and I need to fire this into an entire database, not manually work on each table running the script N times.
This may seem a bit ... ugly. But maybe simply dump the database to a sql/txt file, replace all strings and recreate the database using the modified dump.
You could run the following code to create all the udpate statements you would need to run to do your updates. It would update every field in every table within your database. You would need to run this code, and copy the results and run them.
WARNING - Be sure to test this in a test environment. You don't want any unpleasant surprises. Modify as needed.
SELECT concat('UPDATE ', TABLE_NAME, ' SET ', COLUMN_NAME, ' = REPLACE(', COLUMN_NAME, ', ''STRING_TO_REPLACE'', ''REPLACE_STRING'')')
FROM INFORMATION_SCHEMA.COLUMNS c
WHERE TABLE_SCHEMA = 'YOUR_DATABASE_NAME'
AND DATA_TYPE IN ('char', 'varchar')
;
I limited this to only char and varchar fields. You may need to add additional data types.
I would consider querying INFORMATION_SCHEMA.COLUMNS and dynamically searching the columns and tables. Try something like creating a cursor of all the columns and tables in the db:
DECLARE col_names CURSOR FOR
SELECT column_name, table_name
FROM INFORMATION_SCHEMA.COLUMNS
Then iterate through each of the columns in each of the tables and run dynamic/prepared sql to update where the string exists.
Here are a couple of good posts to get you in the right direction:
https://stackoverflow.com/a/4951354/1073631
https://stackoverflow.com/a/5728155/1073631

search simple way to alter multi tables in one time in mysql

i have a lot of tables that start with some prefix ,
and i want to alter this tables
what is the simple way to do this (instead run over all tables)
i mean something like :
ALTER TABLE LIKE tablenameprefix% ADD INDEX `NewIndex1` (`field`);
how can i do this ?
thanks
EDIT :
can i do a kind of loop not in stored procedure ?
by select the names of tables from
SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_NAME LIKE 'tableprefix%'
You can't. What you could do though is write a stored procedure that enumerates all tables looking for your prefix and performs the necessary changes.
Given that ALTER TABLE syntax doesn't allow multiple table names, you cannot do this. You need to go through all tables in turn:
ALTER [IGNORE] TABLE tbl_name
alter_specification [, alter_specification]
Link: http://dev.mysql.com/doc/refman/5.0/en/alter-table.html
When I wanted to change multiple table's engine from MyISAM to InnoDB, instead of writing a loop I just made a full DB dump and opened it in a text editor. In the text editor I just simply changed all MyISAM words to InnoDB.
I know that this ain't proper solution but for me it was easier then writing a routine for this.
You would have to write a loop, according to the documentation you just specify the table name.
I made a stupid mistake of putting all Wordpress tables with my product tables, fortunately all Wordpress tables start with a wp_ prefix and all my other product tables have no this wp_ prefix.
I created another database named wordpress, now I want to move all tables start with wp_ to that database.
Here is what I did:
SELECT CONCAT('ALTER TABLE olddb.', table_name, ' RENAME wordpress.', table_name, ';')
FROM information_schema.tables
WHERE table_schema='olddb' AND table_name LIKE 'wp%'
INTO OUTFILE '/tmp/move_to_wordpress';
SOURCE /tmp/move_to_wordpress;
That's it.