MySQL Table - MySQL foreign key

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()

 

Guess you like

Origin www.cnblogs.com/gaogao67/p/11649874.html