程序访问数据库服务
前面,我们都是通过命令行客户端对数据库进行操作的。
这个命令行客户端 是一个特殊的 数据库客户端程序, 它能够把我们的SQL语句发送给 MySQL数据库服务,然后把运行结果展示在屏幕上。
大部分的数据库客户端程序,是要完成特殊的功能的。 典型的就是我们后面要学习的 web服务端程序。
我们后面的教程会教大家开发一个 医药公司的 订单管理系统 BYCRM。
这个系统要完成各种业务功能,比如 药品的添加、客户的添加、订单的创建等等,当然要大量地使用数据库服务,它也是一个数据库客户端程序。
我们现在先学习一下 自己开发简单的程序,来使用数据库服务。
我们的应用程序要访问数据库,其实是访问数据服务进程,这个数据服务进程再去读写磁盘上的数据文件。
我们的应用 通常需要调用一个客户端库,也就是下图中的驱动模块, 通过网络访问数据库服务。
安装客户端库
目前使用Python语言访问mysql数据库, 推荐使用客户端库 mysqlclient
和 PyMySQL
PyMySQL 是纯Python实现的。
而 mysqlclient 库是C语言开发的,性能相对高一些。
这两个库都遵循 Python统一数据库访问接口规范(参考PEP 249)
既然是Python的第三方库,当然是用 pip安装了。
运行命令 pip install mysqlclient
安装 mysqlclient,
如果你想使用 的客户端库是 PyMySQL ,运行命令 pip install PyMySQL
安装
读取数据库表内容
我们来看下面这样的一个使用 mysqlclient 库读取数据库表的例子。
import MySQLdb
# 创建一个 Connection 对象,代表了一个数据库连接
connection = MySQLdb.connect(
host="192.168.0.100",# 数据库IP地址
user="username", # mysql用户名
passwd="xxxxx", # mysql用户登录密码
db="dbname" , # 数据库名
# 如果数据库里面的文本是utf8编码的,
#charset指定是utf8
charset = "utf8")
# 返回一个 Cursor对象
c = connection.cursor()
# 执行一个获取 users 表中所有记录的 sql 语句
c.execute("""SELECT * FROM users """)
# rowcount属性记录了最近一次 execute 方法获取的数据行数
numrows = c.rowcount
for x in range(numrows):
# fetchone 方法返回的是一个元组,
# 代表获取的一行记录,元组里面每个元素代表一个字段
row = c.fetchone()
print(row)
如果你使用 的客户端库是 PyMySQL,只有前面 导入库和连接数据库服务 的代码略有差别,像这样
import pymysql
# 创建一个 Connection 对象,代表了一个数据库连接
connection = pymysql.connect(
host="192.168.0.100",
user="username",
password="xxxxx", # mysql用户登录密码
database="dbname" , # 数据库名
charset = "utf8")
通常也就是 password、 database 两个参数不太一样,其它常规参数都一样。
后面的代码和使用mysqlclient的代码都是一样的。
我们可以发现,对数据库的操作是 通过SQL语句
进行的。
我们的代码需要先创建一个 Connection 对象 , 然后再通过Connection 对象创建一个Cursor 对象。
最后使用Cursor对象的execute方法,传入要数据库服务执行的SQL语句。
调用execute执行完SQL语句后,cursor 对象的 fetchone 方法是获取一行记录。
fetchone 方法返回的是一个元组,代表获取的一行记录,元组里面每个元素代表一个字段。
上面的代码通过一个for循环,可以依次获取到数据库的记录行。
我们还可以用 fetchmany 方法来获取多行记录,该方法的参数就是要获取记录的条数,比如
# 执行一个获取 users 表中所有记录的 sql 语句
c.execute("""SELECT * FROM users """)
# fetchmany方法返回的是一个元组,
# 里面每个元素也是元组,代表一行记录
rows = c.fetchmany(2)
print(rows)
我们还可以用 fetchall 方法来获取所有记录,比如
# 执行一个获取 users 表中所有记录的 sql 语句
c.execute("""SELECT * FROM users """)
# fetchall方法返回的是一个元组,
# 里面每个元素也是元组,代表一行记录
rows = c.fetchall()
print(rows)
插入数据到数据库表
我们来看下面这样的一个插入数据到数据库表的例子。
import MySQLdb
# 创建一个Connection 对象,代表了一个数据库连接
connection = MySQLdb.connect(
host="192.168.0.100",# 数据库IP地址
user="username", # mysql用户名
passwd="xxxxx", # mysql用户登录密码
db="dbname" , # 数据库名
# 如果数据库里面的文本是utf8编码的,
#charset指定是utf8
charset = "utf8")
# 返回一个cursor对象
c = connection.cursor()
# 插入一行数据到 user 表中
c.execute(f"INSERT INTO users ( name, nickname, phone) VALUES ('baiyueheiyu', '白月黑羽', '13312345678')")
# 注意 一定要commit,否则添加数据不生效
connection.commit()
connection.close()
插入数据操作当然也是通过 Cursor对象的execute方法,传入要数据库服务执行的 插入操作对应的SQL语句
。
注意, 凡是执行 更改
数据的SQL语句,包括:插入、修改、删除, 后面一定要调用connection的commit方法,否则不生效。
获得插入行自增字段值
通过前面的学习,我们知道,通常数据库表会设计一个名为 id 的主键字段, 这个字段类型通常是 AUTO_INCREMENT
执行插入语句时,我们不知道这个id 值是多少,这是由数据库自动增长的。
但是我们的程序有时需要知道 插入成功后的id值。因为后面的SQL语句可能会用到这个id。
这时可以通过 cursor对象的 lastrowid
属性获取。
如果执行了 INSERT 语句 插入数据到数据库表,并且该表有 AUTO_INCREMENT 字段, cursor对象的lastrowid 就会保存自增的字段值
示例如下
# 插入一行数据到 user 表中,该表有id字段为 AUTO_INCREMENT 类型
c.execute(f"INSERT INTO users ( name, nickname, phone) VALUES ('baiyueheiyu', '白月黑羽', '13312345678')")
# 打印出AUTO_INCREMENT字段id的值
print(c.lastrowid)
lastrowid 的具体信息参考官方文档这里
SQL语句中使用变量
Python程序执行SQL语句,经常需要把一些变量值 填入SQL语句。
很自然的想法的是:直接使用Python的字符串格式化, 比如
# 查询使用变量
name = input('请输入查询姓名')
c.execute(f"SELECT * FROM users WHERE username='{name}'")
# 插入使用变量
info = input('请输入登录名、姓名、电话,以+隔开')
username,realname,phone = info.split('+')
c.execute(f"""INSERT INTO users (name, nickname, phone)
VALUES ('{username}', '{realname}', '{phone}')""")
# 注意 一定要commit,否则添加数据不生效
connection.commit()
当然可以。
但是如果你的Python程序是网站服务,这些参数是前端传递过来的,这种做法不是很推荐,有被 SQL 注入攻击
的危险。
可以使用参数化方式,如下
# 查询使用变量
name = input('请输入查询姓名')
c.execute(f"SELECT * FROM users WHERE username=%s", (name,))
# 插入使用变量
info = input('请输入登录名、姓名、电话,以+隔开')
username,realname,phone = info.split('+')
c.execute(f"""INSERT INTO users (name, nickname, phone)
VALUES (%s, %s, %s)""" , (username,realname,phone))
注意: Python MySQL 语句参数化, 所有占位符写法 都是 %s
,不管这个参数是 字符串类型 还是数字类型、日期类型等等。
除了使用 %s
占位,还可以有其他方式,具体参考这里