《MySQL 入门教程》第 36 篇 Python 访问 MySQL

Posted 不剪发的Tony老师

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《MySQL 入门教程》第 36 篇 Python 访问 MySQL相关的知识,希望对你有一定的参考价值。

本篇我们介绍如何利用 Python DB API 连接和操作 mysql 数据库,包括数据的增删改查操作、存储过程调用以及事务处理等。

Python 是一种高级、通用的解释型编程语言,以其优雅、准确、 简单的语言特性,在云计算、Web 开发、自动化运维、数据科学以及机器学习等人工智能领域获得了广泛应用。

Python 定义了连接和操作数据库的标准接口 Python DB API。不同的数据库在此基础上实现了特定的驱动,这些驱动都实现了标准接口。

MySQL Connector/Python 是官方提供的 Python 访问 MySQL 驱动程序,接下来我们介绍如何通过该驱动连接和操作 MySQL 数据库。本文使用 Python 3。

36.1 连接数据库

MySQL Connector/Python

MySQL Connector/Python 支持 MySQL 提供的所有功能,包括 Python 和 MySQL 数据类型转换,MySQL 扩展语法等。另外,它还支持数据流的压缩,TCP/IP 连接以及 SSL 安全连接。

MySQL Connector/Python 是纯 Python 实现的 API,不需要安装 MySQL客户端代码库或者任何非标准的 Python 模块。

下表列出了兼容的 Connect/Python、MySQL 以及 Python 版本:

Connector/PythonMySQL ServerPython
8.08.0, 5.7, 5.6, 5.53.6, 3.5, 3.4, 2.7
2.25.7, 5.6, 5.53.5, 3.4, 2.7
2.15.7, 5.6, 5.53.5, 3.4, 2.7, 2.6
2.05.7, 5.6, 5.53.5, 3.4, 2.7, 2.6
1.25.7, 5.6, 5.5 (5.1, 5.0, 4.1)3.4, 3.3, 3.2, 3.1, 2.7, 2.6

安装 MySQL Connector/Python 之前需要满足以下条件:

  • 使用管理员权限执行安装;
  • 已经安装 Python,同时将安装目录添加到系统环境变量 PATH。

MySQL Connector/Python 可以从 pypi.org 网站下载,或者我们可以直接使用 pip 工具进行安装。

pip install mysql-connector-python

或者也可以指定安装具体的驱动版本:

pip install mysql-connector-python==8.0.28

以下命令可以用于卸载 MySQL Connector/Python:

pip uninstall mysql-connector-python

执行后系统会提示确认信息:

Proceed (y/n)? y

输入 y 确认卸载。

安装完成后,打开 Python 命令行,输入以下代码验证驱动安装并成功连接 MySQL 数据库:

>>> import mysql.connector
>>> mysql.connector.connect(host='localhost',database='mysql',user='root',password='your pass')

如果屏幕显示类似以下信息,说明已经成功安装 MySQL Connector/Python 驱动:

<mysql.connector.connection.MySQLConnection object at 0x0187AE50>

使用 connect() 函数连接 MySQL

新建一个 Python 文件 connect.py,输入以下代码:

import mysql.connector
from mysql.connector import Error


def connect():
    """ Connect to MySQL database """
    conn = None
    try:
        conn = mysql.connector.connect(host='192.168.56.104',
                                       database='hrdb',
                                       user='tony',
                                       password='tony')
        if conn.is_connected():
            print('Connected to MySQL database')

    except Error as e:
        print(e)

    finally:
        if conn is not None and conn.is_connected():
            conn.close()


if __name__ == '__main__':
    connect()

以上代码包含的步骤如下:

  • 首先,从 MySQL Connector/Python 包中导入 mysql.connector 和 Error 对象;
  • 其次,使用 connect() 函数连接 MySQL 服务器。connect() 函数包含 4 个参数:host、database、user 以及 password。示例中的 connect() 函数建立了一个 python_mysql 数据连接,并且返回了一个 MySQLConnection 对象。
  • 再次,使用 is_connected() 方法检查数据库连接是否成功。如果产生任何异常,例如 MySQL 服务不可用、数据库不存在或者无效的用户名/密码,Python 将会抛出这个异常。try except 代码库处理并显示异常信息。
  • 最后,使用 close() 方法关闭数据库连接。

输入以下命令测试 connect.py 模块:

>python connect.py
Connected to MySQL database

如果用户名或者密码无效,将会返回以下错误:

1045 (28000): Access denied for user 'root'@'localhost' (using password: YES)

如果 MySQL 服务不可用,将会返回以下错误:

2003: Can't connect to MySQL server on 'localhost:3306' (10061 No connection could be made because the target machine actively refused it)

在以上示例中,我们在代码中硬编码了数据库的配置信息,例如 localhost、python_mysql、root 等,这不是一种好的方法,我们应该使用数据库配置文件存储这些信息。

使用 MySQLConnection 对象连接 MySQL

首先,创建一个数据库配置文件 config.ini,定义数据库相关的参数:

[mysql]
host = 192.168.56.104
port = 3305
database = hrdb
user = tony
password = tony

其次,创建一个新的模块 python_mysql_dbconfig.py,用于读取 config.ini 文件中的配置信息并返回一个字典对象:

from configparser import ConfigParser


def read_db_config(filename='config.ini', section='mysql'):
    """ Read database configuration file and return a dictionary object
    :param filename: name of the configuration file
    :param section: section of database configuration
    :return: a dictionary of database parameters
    """
    # create parser and read ini configuration file
    parser = ConfigParser()
    parser.read(filename)

    # get section, default to mysql
    db = 
    if parser.has_section(section):
        items = parser.items(section)
        for item in items:
            db[item[0]] = item[1]
    else:
        raise Exception('0 not found in the 1 file'.format(section, filename))

    return db

我们使用了 ConfigureParser 包读取配置文件。在 Python 解释器中测试 python_mysql_dbconfig 模块:

>>> from python_mysql_dbconfig import read_db_config
>>> read_db_config()
'host': '192.168.56.104', 'port': '3306', 'database': 'hrdb', 'user': 'tony', 'password': 'tony'

然后创建一个新的模块 connect2.py,使用 MySQLConnection 对象连接 python_mysql 数据库:

from mysql.connector import MySQLConnection, Error
from python_mysql_dbconfig import read_db_config


def connect():
    """ Connect to MySQL database """

    db_config = read_db_config()
    conn = None
    try:
        print('Connecting to MySQL database...')
        conn = MySQLConnection(**db_config)

        if conn.is_connected():
            print('Connection established.')
        else:
            print('Connection failed.')

    except Error as error:
        print(error)

    finally:
        if conn is not None and conn.is_connected():
            conn.close()
            print('Connection closed.')


if __name__ == '__main__':
    connect()

以上代码包含的步骤如下:

  • 首先,从 MySQL Connector/Python 包中导入 MySQLConnection 以及 Error 对象,从 python_mysql_dbconfig 模块中导入 read_db_config。
  • 然后,读取数据库配置信息,创建一个新的 MySQLConnection 实例对象。其他内容和上文中的示例类似。

测试一下 connect2 模块:

>python connect2.py
Connecting to MySQL database...
connection established.
Connection closed.

36.2 查询数据

在 Python 代码中查询 MySQL 数据的步骤如下:

  1. 连接 MySQL 数据库,返回一个 MySQLConnection 对象;
  2. 使用 MySQLConnection 对象初始化一个 MySQLCursor 游标对象;
  3. 调用游标的 execute() 方法执行查询;
  4. 使用 fetchone()、fetchmany() 或者 fetchall() 方法从结果集中获取数据;
  5. 调用相关对象的 close() 方法关闭游标和数据库连接。

fetchone() 方法

fetchone() 方法用于从结果集中返回下一条记录,如果没有记录时返回 None。例如:

from mysql.connector import MySQLConnection, Error
from python_mysql_dbconfig import read_db_config


def query_with_fetchone():
    try:
        dbconfig = read_db_config()
        conn = MySQLConnection(**dbconfig)
        cursor = conn.cursor()
        cursor.execute("SELECT * FROM department")

        row = cursor.fetchone()

        while row is not None:
            print(row)
            row = cursor.fetchone()

    except Error as e:
        print(e)

    finally:
        cursor.close()
        conn.close()


if __name__ == '__main__':
    query_with_fetchone()

代码中的具体步骤如下:

  1. 连接 MySQL 数据库,返回一个 MySQLConnection 对象;
  2. 使用 MySQLConnection 对象初始化一个 MySQLCursor 游标对象;
  3. 执行查询语句,查找数据表 department 中的全部数据行;
  4. 调用 fetchone() 方法从结果集中获取第一行数据。然后在 while 循环中显示数据行的内容并获取下一行数据,直到返回所有数据行;
  5. 调用相关对象的 close() 方法关闭游标和数据库连接。

示例中的 read_db_config() 函数来自上文中的 python_mysql_dbconfig.py 模块。

fetchall() 方法

如果查询返回的数据行较少,可以使用 fetchall() 方法一次性从数据库返回所有数据。例如:

from mysql.connector import MySQLConnection, Error
from python_mysql_dbconfig import read_db_config


def query_with_fetchall():
    try:
        dbconfig = read_db_config()
        conn = MySQLConnection(**dbconfig)
        cursor = conn.cursor()
        cursor.execute("SELECT * FROM department")
        rows = cursor.fetchall()

        print('Total Row(s):', cursor.rowcount)
        for row in rows:
            print(row)

    except Error as e:
        print(e)

    finally:
        cursor.close()
        conn.close()


if __name__ == '__main__':
    query_with_fetchall()

代码逻辑和上一个示例类似,除了使用 fetchall() 方法替代 fetchone() 方法。因为我们返回了数据表 department 中的全部记录,可以通过游标对象的 rowcount 数据获取返回的总行数。

fetchmany() 方法

对于相对大一些的查询结果,返回整个查询结果集需要更多时间;而且 fetchall() 需要足够多的内存存储完整结果,可能导致效率问题。

为此,MySQL Connector/Python 接口提供了 fetchmany() 方法,可以获取查询结果中的下一行或者下一批数据行,从而达到返回时间和内存占用的平衡。

首先,定义一个生成器,将数据库调用拆分成一系列的 fetchmany() 方法调用:

def iter_row(cursor, size=10):
    while True:
        rows = cursor.fetchmany(size)
        if not rows:
            break
        for row in rows:
            yield row

其次,调用 iter_row() 生成器每次获取 10 行数据:

def query_with_fetchmany():
    try:
        dbconfig = read_db_config()
        conn = MySQLConnection(**dbconfig)
        cursor = conn.cursor()

        cursor.execute("SELECT * FROM employee")

        for row in iter_row(cursor, 10):
            print(row)

    except Error as e:
        print(e)

    finally:
        cursor.close()
        conn.close()

36.3 插入数据

使用 MySQL Connector/Python 接口插入数据主要包含以下步骤:

  1. 连接 MySQL 数据库,返回一个 MySQLConnection 对象;
  2. 使用 MySQLConnection 对象初始化一个 MySQLCursor 游标对象;
  3. 执行 INSERT 语句插入数据并提交事务;
  4. 关闭数据库连接。

插入单行数据

我们首先在数据库 hrdb 中创建一个新的数据表 books:

-- 通过 SQL 脚本创建表
CREATE TABLE books(
  id INTEGER NOT NULL AUTO_INCREMENT,
  title VARCHAR(50) NOT NULL,
  isbn VARCHAR(13) NOT NULL,
  photo BLOB,
  PRIMARY KEY (id)
);

以下示例为数据表 books 插入了一行新数据:

from mysql.connector import MySQLConnection, Error
from python_mysql_dbconfig import read_db_config

def insert_book(title, isbn):
    query = "INSERT INTO books(title,isbn) " \\
            "VALUES(%s,%s)"
    args = (title, isbn)

    try:
        db_config = read_db_config()
        conn = MySQLConnection(**db_config)

        cursor = conn.cursor()
        cursor.execute(query, args)

        if cursor.lastrowid:
            print('last insert id', cursor.lastrowid)
        else:
            print('last insert id not found')

        conn.commit()
    except Error as error:
        print(error)

    finally:
        cursor.close()
        conn.close()

def main():
   insert_book('SQL编程思想','9787121421402')

if __name__ == '__main__':
    main()

主要代码如下:

  • 首先,从 MySQL Connector/Python 包中导入 MySQLConnection 以及 Error 对象,从 python_mysql_dbconfig 模块中导入 read_db_config() 函数;
  • 其中,定义一个新的函数 insert_book(),包含两个参数:title 和 isbn。在该函数内部,构造一个 INSERT 语句(query)和数据(args);
  • 然后,在 try except 代码块中创建一个新连接,执行语句,提交事务。注意,我们需要显示调用 commit() 方法提交事务。如果成功插入一行数据,我们可以使用游标对象的 lastrowid 属性获取自增字段(AUTO_INCREMENT )最后插入的 id。
  • 接着,在 insert_book() 函数的最后关闭游标和数据库连接。
  • 最后,在 main() 函数中调用 insert_book() 函数插入一行数据。

插入多行数据

MySQL INSERT 语句支持一次插入多行数据,例如:

INSERT INTO books(title,isbn)
VALUES ('高性能MySQL(第4版)', '9787121442575'),
       ('Linux是怎样工作的', '9787115581617'),
       ('机器学习', '9787302423287');

对于 Python,我们可以使用 MySQLCursor 对象的 executemany() 方法插入多行数据。例如:

from mysql.connector import MySQLConnection, Error
from python_mysql_dbconfig import read_db_config

def insert_books(books):
    query = "INSERT INTO books(title,isbn) " \\
            "VALUES(%s,%s)"

    try:
        db_config = read_db_config()
        conn = MySQLConnection(**db_config)

        cursor = conn.cursor()
        cursor.executemany(query, books)

        conn.commit()
    except Error as e:
        print('Error:', e)

    finally:
        cursor.close()
        conn.close()

def main():
    books = [('高性能MySQL(第4版)', '9787121442575'),
             ('Linux是怎样工作的', '9787115581617'),
             ('深度学习', '9787302423287')]
    insert_books(books)

if __name__ == '__main__':
    main()

示例中的代码逻辑和上一个示例类似,只是将 execute() 方法替换成了 executemany() 方法。在 main() 函数中,我们传递了一个元组列表,每个元素包含了相应的 title 和 isbn。

36.4 更新数据

使用 MySQL Connector/Python 接口更新数据的操作步骤如下:

  1. 连接 MySQL 数据库,返回一个 MySQLConnection 对象;
  2. 使用 MySQLConnection 对象初始化一个 MySQLCursor 游标对象;
  3. 执行 UPDATE 语句更新数据并提交事务;
  4. 关闭数据库连接。

以下示例通过 id 更新图书的的 title 信息:

from mysql.connector import MySQLConnection, Error
from python_mysql_dbconfig import read_db_config


def update_book(book_id, title):
    # read database configuration
    db_config = read_db_config()

    # prepare query and data
    query = """ UPDATE books
                SET title = %s
                WHERE id = %s """

    data = (title, book_id)

    try:
        conn = MySQLConnection(**db_config)

        # update book title
        cursor = conn.cursor()
        cursor.execute(query, data)

        # accept the changes
        conn.commit()

    except Error as error:
        print(error)

    finally:
        cursor.close()
        conn.close()


if __name__ == '__main__':
    update_book(4, '机器学习')

其中,UPDATE 语句使用了两个占位符(%),分别用于 title 和 id。然后将 UPDATE 语句(query)和 (title,id) 元组传递给 execute() 方法,MySQL Connector/Python 会将查询转换为以下语句:

UPDATE books
SET title = '机器学习'
WHERE id = 4

注意,如果 SQL 语句需要接收用户的输入,一定要使用占位符(%s),这种方法可以阻止 SQL 注入问题。

接下来我们运行一个测试。首先查看 id 等于 4 的数据:

SELECT id, title, isbn
FROM books
WHERE id = 4;

id|title   |isbn         |
--+--------+-------------+
 4|深度学习 |9787302423287|

然后执行 update.py 模块:

python update.py

最后再次查看 id 等于 4 的记录:

SELECT id, title, isbn
FROM books
WHERE id = 4;

id|title   |isbn         |
--+--------+-------------+
 4|机器学习 |9787302423287|

36.5 删除数据

使用 MySQL Connector/Python 接口插入数据主要包含以下步骤:

  1. 连接 MySQL 数据库,返回一个 MySQLConnection 对象;
  2. 使用 MySQLConnection 对象初始化一个 MySQLCursor 游标对象;
  3. 执行 UPDATE 语句更新数据并提交事务;
  4. 关闭数据库连接。

以下示例通过 id 实现图书数据的删除:

from mysql.connector import MySQLConnection, Error
from python_mysql_dbconfig import read_db_config

def delete_book(book_id):
    db_config = read_db_config()

    query = "DELETE FROM books WHERE id = %s"

    try:
        # connect to the database server
        conn = MySQLConnection(**db_config)

        # execute the query
        cursor = conn.cursor()
        cursor.execute(query, (book_id,))

        # accept the change
        conn.commit()

    except Error as error:
        print(error)

    finally:
        cursor.close()
        conn.close()


if __name__ == '__main__':
    delete_book(4)

我们同样在 DELETE 语句中使用了占位符(%)。调用 execute() 方法时传递了 DELETE 语句和 (book_id,) 元组,MySQL Connector/Python 会将删除转换为以下语句:

DELETE FROM books 
WHERE id = 4;

然后运行以上示例。最后再次查询 id 等于 4 的图书,不会返回任何数据,意味着成功删除了一条记录。

36.6 调用存储过程

MySQL Connector/Python 接口支持调用 MySQL 存储过程。

准备工作

第 31 篇介绍了 MySQL 存储过程相关的知识。

我们首先创建两个作为演示的存储过程。以下存储过程返回了全部图书:

USE hrdb;

DELIMITER $$

CREATE PROCEDURE find_all()
BEGIN
	SELECT 
		title, 
		isbn
	FROM books
	ORDER BY title;
END$$

DELIMITER ;

直接调用 find_all() 存储过程:

CALL find_all();

title              |isbn         |
-------------------+-------------+
Linux是怎样工作的   |9787115581617|
SQL编程思想        |9787121421402|
高性能MySQL(第4版)|9787121442575|

第二个存储过程 find_by_isbn() 用于通过 ISBN 查找图书:

DELIMITER $$

CREATE PROCEDURE find_by_isbn(
	IN p_isbn VARCHAR(13),
    OUT p_title VARCHAR(50)
)
BEGIN
	SELECT title 
    INTO p_title 
    FROM books
	WHERE isbn = p_isbn;
END$$

DELIMITER ;

find_by_isbn() 包含两个参数:p_isbn(入参)以及 p_title(出参)。通过传入 ISBN,该存储过程返回对应的图表名称,例如:

CALL find_by_isbn('9787121421402',@title);
SELECT @title;

@title    |
----------+
SQL编程思想|

Python 调用存储过程

在 Python 代码中调用存储过程的步骤如下:

  1. 连接 MySQL 数据库,返回一个 MySQLConnection 对象;
  2. 使用 MySQLConnection 对象初始化一个 MySQLCursor 游标对象;
  3. 调用游标对象的 callproc() 方法,第一个参数为存储过程名称。如果存储过程需要参数,以列表的形式作为第二个参数传递给callproc() 方法。如果存储过程返回了结果集,可以调用游标的 stored_results() 获取一个列表迭代器对象,然后通过 fetchall() 方法遍历结果。
  4. 关闭数据库连接。

以下示例调用了 find_all() 存储过程并打印返回结果:

from mysql.connector import MySQLConnection, Error
from python_mysql_dbconfig import read_db_config


def call_find_all_sp():
    try:
        db_config = read_db_config()
        conn = MySQLConnection(**db_config)
        cursor = conn.cursor()

        cursor.callproc('find_all')

        # print out the result
        for result in cursor.stored_results():
            print(result.fetchall())

    except Error as e:
        print(e)

    finally:
        cursor.close()
        conn.close()


if __name__ == '__main__':
    call_find_all_sp()

下面的示例调用了 find_by_isbn() 存储过程:

from mysql.connector import MySQLConnection, Error
from python_mysql_dbconfig import read_db_config


def call_find_by_isbn():
    try:
        db_config = read_db_config()
        conn = MySQLConnection(**db_config)
        cursor = conn.cursor()

        args = ['9787121421402', 0]
        result_args = cursor以上是关于《MySQL 入门教程》第 36 篇 Python 访问 MySQL的主要内容,如果未能解决你的问题,请参考以下文章

《MySQL 入门教程》第 36 篇 Python 访问 MySQL

Python入门教程第36篇 可遍历对象与迭代器

Python入门(目录全览)

python 数据库编程,这篇是针对 mysql 的,滚雪球学Python第4季第13篇

python 数据库编程,这篇是针对 mysql 的,滚雪球学Python第4季第13篇

MySQL基础篇MySQL数据库安装教程