In previous MySQL operation and maintenance, it is required to disable some features common database triggers / stored procedures / foreign keys and so on, so the foreign key MySQL is relatively unfamiliar, specially under inquiry today.
Existing table TB001 and TB002 6291456 each contain rows of data, create the following script:
CREATE TABLE `tb001` ( `ID` int(11) NOT NULL AUTO_INCREMENT, `C1` int(11) DEFAULT NULL, PRIMARY KEY (`ID`) ) ENGINE=InnoDB AUTO_INCREMENT=6684558 DEFAULT CHARSET=utf8; CREATE TABLE `tb002` ( `ID` int(11) NOT NULL AUTO_INCREMENT, `C1` int(11) DEFAULT NULL, `C2` int(11) DEFAULT NULL, PRIMARY KEY (`ID`) ) ENGINE=InnoDB AUTO_INCREMENT=6356897 DEFAULT CHARSET=utf8;
Increase in column C1 now TB002 a foreign key to the primary key TB001, there is created the following three ways:
Embodiment 1: directly create foreign key
Query: the ALTER TABLE TB002 the ADD CONSTRAINT `FK_C1` a FOREIGN KEY (` C1') the REFERENCES `TB001` (` ID`) a total of 6291456 rows were affected by the implementation of time-consuming: 55.889 sec transmission time: 1.003 sec Total time: 56.892 sec
Option 2: Create a foreign key index + Create
Query: the ALTER the Table TB002 the ADD INDEX FK_C1 (C1) Total 0 rows affected time-consuming: 9.676 sec transmission time: 1.001 sec Total time: 10.678 sec - --------------- --------------------------------- inquiry: the ALTER TABLE TB002 the ADD CONSTRAINT `FK_C1` a FOREIGN KEY (` C1') the REFERENCES `TB001` (` ID`) co- 6291458 rows affected time-consuming: . 1 min . 9 sec transmission time: 1.001sec Total time: . 1 min 10 sec
Mode 3: Create create a foreign key index + + FOREIGN_KEY_CHECKS
Query: the ALTER the Table TB002 the ADD INDEX FK_C1 (C1) Total 0 rows affected time-consuming: 9.393 sec transmission time: 1.002 sec Total time: 10.396 sec - --------------- --------------------------------- inquiry: the SET @@ FOREIGN_KEY_CHECKS = 0 total 0 rows affected time-consuming: 0 sec transfer time: 0 sec total time: 0 sec - ----------------------------------- ------------- inquiry: the ALTER TABLE TB002 the ADD CONSTRAINT `FK_C1` a FOREIGN KEY (` C1') the REFERENCES `TB001` (` ID`) Total 0 rows affected time-consuming: 0.002 sec transmission time: 0.001 sec Total time: 0.004 sec - --- --------------------------------------------- inquiry: the SET @@ FOREIGN_KEY_CHECKS = . 1 total 0 rows affected time-consuming: 0 sec transmission time: 0 sec total time: 0 sec
If you do not set FOREIGN_KEY_CHECKS = 0, in the foreign key creation process and can block other sessions CRUD operations on the table TB002's (deletion not allowed).
It is found from the results of the above three ways to create foreign key fastest way: creating a foreign key index + + FOREIGN_KEY_CHECKS create, when setting FOREIGN_KEY_CHECKS = 0, to create foreign key has no check data in the current table, belonging metadata operations Therefore done quickly, less impact on other sessions, but can not guarantee that data in the table to meet the foreign key constraint.
FOREIGN_KEY_CHECKS related
FOREIGN_KEY_CHECKS official document explains:
If set to 1 (the default), foreign key constraints for InnoDB tables are checked. If set to 0, foreign key constraints are ignored, with a couple of exceptions. When re-creating a table that was dropped, an error is returned if the table definition does not conform to the foreign key constraints referencing the table. Likewise, an ALTER TABLE operation returns an error if a foreign key definition is incorrectly formed. Setting foreign_key_checks to 0 also affects data definition statements: DROP SCHEMA drops a schema even if it contains tables that have foreign keys that are referred to by tables outside the schema, and DROP TABLE drops tables that have foreign keys that are referred to by other tables. Setting foreign_key_checks to 1 does not trigger a scan of the existing table data. Therefore, rows added to the table while foreign_key_checks=0 will not be verified for consistency.
When FOREIGN_KEY_CHECKS set to 0, the foreign key constraint will be ignored, and will not trigger when FOREIGN_KEY_CHECKS set to 1 constraint checking.
Remove the foreign key
## Remove the foreign key, but dependent on foreign key indexes are not deleted the ALTER TABLE DROP a FOREIGN KEY TB002 FK_C1; ## delete the foreign key dependency index ALTER TABLE TB002 DROP INDEX FK_C1;
Examples of the foreign key in the export
# coding: utf-8 import pymysql import os, traceback, datetime pymysql.install_as_MySQLdb() import MySQLdb working_folder = os.path.dirname(os.path.abspath(__file__)) default_log_file = os.path.join(working_folder, "default_log.txt") error_log_file = os.path.join(working_folder, "error_log.txt") create_key_file = os.path.join(working_folder, "create_key_script.txt") drop_key_file = os.path.join(working_folder, "drop_key_script.txt") class MySQLForeignKeyLoader(object): def __init__(self, mysql_host, mysql_port, mysql_user, mysql_password, mysql_charset, database_name, connect_timeout=60): self.mysql_host = mysql_host self.mysql_port = mysql_port self.mysql_user = mysql_user self.mysql_password = mysql_password self.mysql_charset = mysql_charset self.database_name = database_name self.connect_timeout=connect_timeout The DEF highlight (Self, Message): return " % S [30; 2M% S% S [1M " % (CHR (27), Message, CHR (27 )) DEF print_warning_message (Self, Message): "" " In red font message content : param message: message content : return: None return value "" " message = STR (message) Print (self.highlight ( '' ) + " % S [31 is; 1M% S% S [0m " % (CHR (27), Message, CHR (27 ))) self.write_file (the error_log_file, Message) DEF print_info_message (Self, Message): "" " cautionary message is output in green : param message: Message Content : return: None Return Value " "" Message = STR (Message) Print (self.highlight ( '' ) + " S% [32; S% S% 2M [0m " % (CHR (27), Message, CHR (27 ))) self.write_file (default_log_file, Message) DEF write_file (Self, file_path, Message): " "" the additional incoming message is written to the file specified by file_path first create a file's directory : param file_path: the file path to write : to write information: param message : return: """ file_handle = Open (file_path, ' self.mysql_port,A ' ) file_handle.writelines (Message) # append a wrap to facilitate browsing file_handle.writelines (CHR (10 )) file_handle.flush () file_handle.close () DEF get_mysql_connection (Self, is_use_dict = False): "" " The default returns the configuration database connection : return: database connection "" " IF is_use_dict: Conn = MySQLdb.connect ( Host = self.mysql_host, Port = User = self.mysql_user, the passwd =self.mysql_password, connect_timeout=self.connect_timeout, charset=self.mysql_charset, db=self.database_name, cursorclass=pymysql.cursors.DictCursor ) else: conn = MySQLdb.connect( host=self.mysql_host, port=self.mysql_port, user=self.mysql_user, passwd=self.mysql_password, connect_timeout=self.connect_timeout, charset=self.mysql_charset, db=self.database_name, cursorclass=pymysql.cursors.Cursor ) return conn def mysql_query(self, sql_script, sql_paras=None, is_use_dict=True): """ 执行SQL :param sql_script: :param sql_paras: :param is_use_dict: :return: """ cursor = None mysql_conn = None try: mysql_conn = self.get_mysql_connection(is_use_dict=is_use_dict) cursor = mysql_conn.cursor() if sql_paras is not None: cursor.execute(sql_script, sql_paras) else: cursor.execute(sql_script) exec_result = cursor.fetchall() return exec_result except Exception as ex: warning_message = """ mysql query exception, sql script:{sql_script} sql paras:{sql_paras} exception:{sql_exception} trace back:{sql_trace} """.format( sql_script=sql_script, sql_paras=str(sql_paras), sql_exception=str(ex), sql_trace=str(traceback.format_exc()) ) self.print_warning_message(warning_message) raise Exception(str(ex)) finally: if cursor is not None: cursor.close() if mysql_conn is not None: mysql_conn.close() def mysql_exec(self, sql_script, sql_paras=None): """ 执行SQL :param sql_script: :param sql_paras: :return: """ cursor = None mysql_conn = None try: mysql_conn = self.get_mysql_connection(is_use_dict=False) cursor = mysql_conn.cursor() if sql_paras is not None: cursor.execute(sql_script, sql_paras) else: cursor.execute(sql_script) affect_rows = cursor.rowcount mysql_conn.commit() return affect_rows except Exception as ex: warning_message = """ mysql query exception, sql script:{sql_script} sql paras:{sql_paras} exception:{sql_exception} trace back:{sql_trace} """.format( sql_script=sql_script, sql_paras=str(sql_paras), sql_exception=str(ex), sql_trace=str(traceback.format_exc()) ) self.print_warning_message(warning_message) raise Exception(str(ex)) finally: if cursor is not None: cursor.close() if mysql_conn is not None: mysql_conn.close() def get_foreign_keys(self): sql_script = """ SELECT * FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS WHERE CONSTRAINT_SCHEMA NOT IN('sys','mysql','information_schema','proformance_schema') """ return self.mysql_query(sql_script=sql_script, is_use_dict=True) def get_drop_foreign_key_script(self, source_database_name, source_table_name, foreign_key_name): sql_script = "ALTER TABLE `{database_name}`.`{table_name}` DROP FOREIGN KEY `{foreign_key_name}`;".format( database_name=source_database_name, table_name=source_table_name, foreign_key_name=foreign_key_name ) self.print_info_message(sql_script) return sql_script def get_create_table_script(self, source_database_name, source_table_name): sql_script = "SHOW CREATE TABLE `{database_name}`.`{table_name}`;".format( database_name=source_database_name, table_name=source_table_name ) query_result = self.mysql_query(sql_script=sql_script, is_use_dict=False) if len(query_result) > 0: return query_result[0][1] else: return "" def get_create_foreign_key_script(self, source_database_name, source_table_name, foreign_key_name): table_script = self.get_create_table_script( source_database_name=source_database_name, source_table_name=source_table_name ) table_script = table_script.encode("utf-8") script_items = table_script.splitlines() constraint_script = "" for script_item in script_items: if script_item.lower().find(str(foreign_key_name).lower()) > 0 \ and script_item.lower().find(" FOREIGN KEY ".lower()) > 0 \ and script_item.lower().find(" CONSTRAINT ".lower()) > 0 \ and script_item.lower().find(" REFERENCES ".lower()) > 0: constraint_script = script_item break if constraint_script <> "": foreign_key_script = "ALTER TABLE `{database_name}`.`{table_name}` ADD {constraint_script};".format( database_name=source_database_name, table_name=source_table_name, constraint_script=constraint_script ) else: foreign_key_script = "## Can not load script for foreign key `{foreign_key_name}` in `{database_name}`.`{table_name}`;".format( database_name=source_database_name, table_name=source_table_name, foreign_key_name=foreign_key_name ) self.print_info_message(foreign_key_script) return foreign_key_script def load_foreign_key_scripts(self): self.print_info_message("start to load script for foreign keys") foreign_key_list = self.get_foreign_keys() for foreign_key_item in foreign_key_list: source_database_name = foreign_key_item["CONSTRAINT_SCHEMA"] source_table_name = foreign_key_item["TABLE_NAME"] foreign_key_name = foreign_key_item["CONSTRAINT_NAME"] self.print_info_message( "load script for foreign key `{foreign_key_name}` in `{source_database_name}`.`{source_table_name}`".format( source_database_name=source_database_name, source_table_name=source_table_name, foreign_key_name=foreign_key_name )) drop_script = self.get_drop_foreign_key_script( source_database_name=source_database_name, source_table_name=source_table_name, foreign_key_name=foreign_key_name ) self.write_file(drop_key_file, drop_script) create_script = self.get_create_foreign_key_script( source_database_name=source_database_name, source_table_name=source_table_name, foreign_key_name=foreign_key_name ) self.write_file(create_key_file, create_script) self.print_info_message("end to load script for foreign keys") def main(): loader = MySQLForeignKeyLoader( mysql_host="192.168.0.1", mysql_port=3306, mysql_user="root", mysql_password="root", mysql_charset="utf8", database_name="mysql" ) loader.load_foreign_key_scripts() if __name__ == '__main__': main()