Python操作SQL的两种方式
pymyql
PyMySQL 是在 Python3.x 版本中用于连接 MySQL 服务器的一个库,废话不多说,直接开始。
安装
pip install pymysql
数据库连接
pymysql 提供 connect() 用于数据库连接。我们需要指定以常用参数,方便确定数据库的位置以及编码方式:
- host: Mysql 地址,远程数据库的话用 IP地址
- user:Mysql 用户名
- password:密码
- database:数据库库名
- port :Mysql 端口,如果有手动更改过的话, 一般默认是 3306
- charset:编码方式
#!/usr/bin/env python
# coding: utf-8
import pymysql
host = 'localhost' # 地址远程的话用 IP
user = 'root' # Mysql 用户名
password = '123' # 密码
database = 'Database' # 库名
dbconnect = pymysql.connect(host=host, port=3306, user=user, password=password, database=database,
charset='utf8')
建库建表
pymsql 操作数据库一般有以下三个步骤:
- 连接数据库
- 创建游标对象
- 执行 SQL 语句
- 插入、更新、删除操作需要提交
- 关闭连接
我们可以使用 cursor() 创建游标对象,execute() 执行 SQL 语句。
import pymysql
dbconnect = pymysql.connect(host='localhost', user='root', password='123', database='studet')
dbcursor = dbconnect.cursor() # 创建游标
# 使用预处理语句创建表
sql = """CREATE TABLE student (
NAME CHAR(20) NOT NULL,
AGE INT,
SEX CHAR(1)"""
dbcursor.execute(sql) # 执行 sql 语句
dbconnect.close() # 关闭数据库连接
插入数据
和上面一样,先创建游标对象,再执行 SQL 语句,不过 需要加上 commit。
import pymysql
dbconnect = pymysql.connect(host='localhost', user='root', password='123', database='studet')
dbcursor = dbconnect.cursor() # 创建游标
# SQL 插入语句
sql = """INSERT INTO student (NAME, AGE, SEX)
VALUES ('Ma', 20, 'M')"""
cursor.execute(sql) # 执行sql语句
db.commit() # 提交到数据库执行
db.close() # 关闭数据库连接
更新与删除
所有操作和上面都一样的,只需要修改 SQL。千万不要忘记 commit 操作就行,否则 SQL 语句不会生效。
回滚
对于支持事务的数据库, 在 Python 数据库编程中,当游标建立之时,就自动开始了一个隐形的数据库事务。commit() 方法提交游标的所有更新操作,rollback() 方法回滚 当前游标 的所有操作。每一个方法都开启一个新的事务。
import pymysql
# 打开数据库连接
db = pymysql.connect(host='localhost',
user='testuser',
password='test123',
database='TESTDB')
# 使用cursor()方法获取操作游标
cursor = db.cursor()
# SQL 删除语句
sql = "DELETE FROM EMPLOYEE WHERE AGE > %s" % (20)
try:
# 执行SQL语句
cursor.execute(sql)
# 提交修改
db.commit()
except:
# 发生错误时回滚
db.rollback()
db.close()
总结
不难看看出使用这种方式操作数据库,其实很简单。因为操作数据的重点还是写 SQL 语句。只需要使用函数来执行 SQL 而已。这种方式唯一的难点就是怎么通过参数拼接 SQL 。
但是这种方式如果不对输入进行限制会有比较严重的 SQL注入 风险。
SQL Alchemy
SQLAlchemy 是一个Python语言编写的高性能的关系型数据库ORM框架,它提供了高层的 ORM 和底层的原生数据库的操作。
ORM
ORM 全拼Object-Relation Mapping,中文意为 对象-关系映射。主要实现模型对象到关系数据库数据的映射。
ORM提供了一种面向对象操作数据库的方式给开发者。不需要编写原生SQL语句也能操作数据库,实现了业务代码与底层数据的解耦。
优点:
- 只需要面向对象编程, 不需要面向数据库编写 SQL。
- 对数据库的操作都转化成对类/对象的属性和方法的操作。
- 不用编写各种数据库的
原生sql语句,也可以编写原生SQL语句。
- 实现了数据模型代码与数据库数据的解耦, 屏蔽了不同数据库操作上的差异。
- 不再需要关注当前项目使用的是哪种数据库。
- 通过简单的配置就可以轻松更换数据库,而不需要修改业务代码.
缺点:
- 相比较直接使用SQL语句操作数据库,ORM需要把操作转换成SQL语句,所以有性能损失.
- 根据对象的操作转换成SQL语句,根据查询的结果转化成模型实例对象, 在映射过程中有性能损失.
- 不同的ORM提供的操作不一样,增加了学习成本
安装
pip install SQLAlchemy
数据库连接
from sqlalchemy import create_engine # 数据库引擎
engine = create_engine('mysql+pymysql://root:123@localhost:3306/todo?charset=utf8mb4',
echo=True, pool_size=5, pool_recycle=60 * 60)
部分参数及解释:
- echo:默认为 FALSE,如果为真,将输出转化后的 SQL 语句。
- encoding:编码,默认为
utf-8。 - pool_size:连接池大小,默认为5,设置为0时表示无限制。
- pool_recycle:设置时间以限制数据库多久没连接自动断开。
使用SQL语句操作
sqlalchemy 除了使用面向对象的方式,其实也提供的原生 SQL 操作数据库的方法。
# 方式一、直接使用核心
from sqlalchemy import create_engine # 数据库引擎
from sqlalchemy import text # text 函数用于创建一个 SQL 表达式对象
engine = create_engine('mysql+pymysql://root:123@localhost:3306/todo?charset=utf8mb4',
echo=True, pool_size=5, pool_recycle=60 * 60)
with engine.connect() as con:
ans = con.execute(text('SELECT id, title FROM topic'))
"""
可以在后面参数中,添加SQL的参数如:
conn.execute(
text("INSERT INTO some_table (x, y) VALUES (:x, :y)"),
[{"x": 11, "y": 12}, {"x": 13, "y": 14}],
)
具体看官方参考(上面链接)
"""
print(ans.fetchall())
# 同样如果是更新删除操作需要提交 conn.commit()
# 方式二、使用会话
from sqlalchemy import create_engine # 数据库引擎
from sqlalchemy import text
from sqlalchemy.orm import Session # Session 有多种不同的创建模式
engine = create_engine('mysql+pymysql://root:123@localhost:3306/todo?charset=utf8mb4',
echo=True, pool_size=5, pool_recycle=60 * 60)
with Session(engine) as session:
ans = session.execute(text('SELECT id, title FROM topic'))
print(ans.fetchall())
# 更新删除操作需要提交 session.commit()
声明映射(元数据)
就是我们在 Python 中创建一个类,对应着数据库中的一张表,类的每个属性,就是这个表的字段名。这种的类对应于数据库中表的类,就称为映射类,我们要创建一个映射类来申明一张表。
# 方式一、直接构造
from sqlalchemy import MetaData
from sqlalchemy import Table, Column, Integer, String
metadata_obj = MetaData()
user_table = Table(
"user_account",
metadata_obj,
Column("id", Integer, primary_key=True), # 可以指定主键、外键、默认值、是否为空等约束
Column("name", String(30)),
Column("fullname", String(30)),
)
# 方式二、声明式
from sqlalchemy import create_engine # 数据库引擎
from sqlalchemy import String, Integer
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import mapped_column
engine = create_engine('mysql+pymysql://root:123@localhost:3306/todo?charset=utf8mb4',
echo=True, pool_size=5, pool_recycle=60 * 60)
class Base(DeclarativeBase):
pass
class User(Base):
__tablename__ = "user_account"
id = mapped_column(Integer, primary_key=True)
name = mapped_column(String(30))
fullname = mapped_column(String(30))
def __repr__(self):
return f"User(id={self.id}, name={self.name}, fullname={self.fullname})"
# 在 1.4 及以前版本中作用 Column() 而不是 mapped_column()
# mapped_column() 仅适用于 ORM 声明性映射, 它不能在 Table 构造中使用!
申明好表的话就可以通过 user_table 对象使用各种方法操作表了。
插入数据
# 方式一
from sqlalchemy import create_engine
from sqlalchemy import String, Integer
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import mapped_column
from sqlalchemy import insert
engine = create_engine('mysql+pymysql://root:123@localhost:3306/todo?charset=utf8mb4',
echo=True, pool_size=5, pool_recycle=60 * 60)
class Base(DeclarativeBase):
pass
class User(Base):
__tablename__ = "user_account"
id = mapped_column(Integer, primary_key=True)
name = mapped_column(String(30))
fullname = mapped_column(String(30))
def __repr__(self) -> str:
return f"User(id={self.id!r}, name={self.name!r}, fullname={self.fullname!r})"
# 如果事先没有创建表需要在下面语句创建一下
# Base.metadata.create_all(engine)
ins_SQL = insert(User).values(id=1, name='M', fullname='MJX')
print(ins_SQL)
with engine.connect() as conn:
result = conn.execute(ins_SQL)
conn.commit()
# 方式二(推荐)
from sqlalchemy import create_engine
from sqlalchemy import String, Integer
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import mapped_column
from sqlalchemy.orm import Session # Session 有多种不同的创建
engine = create_engine('mysql+pymysql://root:123@localhost:3306/todo?charset=utf8mb4',
echo=True, pool_size=5, pool_recycle=60 * 60)
class Base(DeclarativeBase):
pass
class User(Base):
__tablename__ = "user_account"
id = mapped_column(Integer, primary_key=True)
name = mapped_column(String(30))
fullname = mapped_column(String(30))
def __repr__(self) -> str:
return f"User(id={self.id!r}, name={self.name!r}, fullname={self.fullname!r})"
user = User(id=2, name='M', fullname='MJX')
with Session(engine) as session:
ans = session.add(user)
session.commit()
查询数据
查询语句和上面的相差其实就不大了。
# 方式一
from sqlalchemy import select
stmt = select(user_table).where(user_table.c.name == "spongebob")
print(stmt)
with engine.connect() as conn:
for row in conn.execute(stmt):
print(row)
# 方式二
with Session(engine) as session:
ans = session.query(User).filter_by(name='ed')
