对关系数据库进行相关操作的 Python 库。如:
pyodbc:封装了 ODBC API,通过它可访问各种数据库PyMysql:首选 Mysql 的 Python 驱动MySQLdb:备用,明明是 MysqlClient,导入的是 MySQLdb,因为是改自它吧
pymssql:操作 sql serverpsycopg:PostgreSQLcx_oracle:操作 OracleSQLAlchemy:既支持原生 SQL,又支持 ORM 的工具peewee:轻量 python orm 库dataset:提供了一个简单的抽象层, 无需完整的 ORM 可省大量 SQL 语句,可像 JSON 和 NoSQL 一样操作数据库Gadfly:提供了一个完整的 SQL 环境sandman2:号称不用一行代码,用 API 操作数据库,不咋地,OUT
概述
Python 能够通过遵循 PythonDB-API(数据库应用程序编程接口)规范的模块与 Microsoft SQLServer,Oracle,Sybase,DB2,MySQL、SQLite 等数据库进行通信。
- 数据库选择,首选 MySQL,其次是其自带的 sqlite。
基本常识
- SQL:通用数据语言,不要低估 sql 的力量(相比之下,pandas 还是有所限制的)
- 表、行、字段…..等关系数据库基本知识,详见 数据库篇。
- Python 开发相关:
- Connection: 要操作关系数据库,首先需要连接到数据库,一个数据库连接称为 Connection;
- **Cursor:**要执行 SQL 语句,需要游标,
Python 定义了一套操作数据库的 API 接口,任何数据库要连接到 Python,只需要提供符合 Python 标准的数据库驱动即可。
- Python 的 DB-API:
Connection和Cursor对象,打开后记得关闭- 使用
Cursor对象执行insert,update,delete语句时,执行结果由rowcount返回影响的行数,就可以拿到执行结果。 - 使用
Cursor对象执行select语句时,通过featchall()可以拿到结果集。结果集是一个 list,每个元素都是一个 tuple,对应一行记录。
如果 SQL 语句带有参数,那么需要把参数按照位置传递给execute()方法,有几个?占位符就必须对应几个参数,例如:
1 | cursor.execute('select * from user where name=? and pwd=?', ('abc', 'password')) |
1 | rpm -q MySQL-python |
连接
1 | conn.begin() # 开启事务 |
游标
1 | # 创建连接 |
1 | # sql="select id,name,content,start_time,end_time,importance,emergency,completion,insert_time,update_time,remarks from t_01001_pending" |
ORM
ORM:Object-Relational Mapping:对象 - 关系映射系统,将关系数据库的表结构映射到对象上。
- SQLAlchemy
- 优点:
- 提高开发效率,降低开发成本
- 使开发更加对象化:将数据库表的一行记录与一个对象互相做自动转换
- 可移植
- 可方便地引入数据缓存之类的附加功能
- 缺点
- 自动化进行关系数据库的映射需要消耗系统性能(但基本可忽略不计)
- 处理多表联查、where 条件复杂时,ORM 的语法会变得复杂。
sqlite3
sqlite3:【标准库】Python 内置的 SQLite3,可直接使用
- 提供了一个可以使用稍微非标准的 SQL 语法更新和访问的持久数据库。
- 特点:
- C 写的,轻量,体积小,可嵌入,数据库为一个文件
- 无服务器、零配置、事务性的 SQL 数据库引擎
- 不能承受高并发
- 常用于桌面和移动应用
sqlite3:Python 自带的轻量级数据库模块
- SQLite:用 C 写的小型、快速、功能齐全的关系灵气库引擎
- 设计目标:嵌入式
- 最大支持 128TiB 数据
数据类型
NULL:空值:None
INTEGER:带符号的整数:int/long
REAL:浮点数:float
TEXT:文本字符串:str/unicode
BLOB:blob 数据:buffer
布尔值被存储为整数 0(false)和 1(true)
- 索引不应该使用在较小的表上。
- 索引不应该使用在有频繁的大批量的更新或插入操作的表上。
- 索引不应该使用在含有大量的 NULL 值的列上。
- 索引不应该使用在频繁操作的列上。
1 | import sqlite3 |
1 |
|
pyodbc
pyodbc:封装了 ODBC API,通过它可访问各种数据库
pymysql
PyMysql:首选 Mysql 的 Python 驱动
MySQLdb:备用,明明是 MysqlClient,导入的是 MySQLdb,因为是改自它吧
- Web 世界中使用最广泛的数据库服务器。
- Mysql 相比 SQLite 高并发性能强,占用内存多。
- MySQL 的工具:首选
pymysql,其次是mysql-connector-python,但mysqllient说是有极致性能,mysql-connector-python是 MySQL 官方提供的,让人纠结… - MySQL 的 SQL 占位符是
%s。 mysql-connector-python安装的时候需要给 pip 命令加上参数--allow-externalpip install --allow-external mysql-connector-python
基本使用
1 | import pymysql |
连接
1 | import pymysql |
插入
1 | try: |
查询
1 | try: |
pymssql
pymssql:操作 sql server
psyscopg
psycopg:PostgreSQL
cx_oracle
cx_oracle:操作 Oracle
SQLAlchemy
SQLAlchemy:既支持原生 SQL,又支持 ORM 的工具
- 最知名的 ORM 框架之一
- 定义 class,将表的内容用 Python 数据结构表示,list 下多个 tuple 表示多行数据。
1 | from sqlalchemy import Column,String,create_engine |
- ORM:将数据库表的行与相应的对象建立关联,互相转换。
1 | class User(Base): |
- 主要组件:
- SQLAlchemy ORM:构建在 Core 之上,以提供可选的 对象关系映身功能。Python 类映射到数据库表、会话的对象持久化机制。
- Object Relation Mapper(ORM)
- SQLAlchemy Core:数据库工具包的基础架构。包括:数据库连接、查询、结果交互及程序化构建 SQL 语句的工具。
- Schema/Types
- SQL Expression Language
- Engine
- Connection Pooling
- Dialect
- DBAPI
- SQLAlchemy ORM:构建在 Core 之上,以提供可选的 对象关系映身功能。Python 类映射到数据库表、会话的对象持久化机制。
- 使用版本 2.0 以上
sqlalchemy.__version__
建立连接 - 引擎
- engine 对象:充当数据库连接的中心来源,提供工厂、连接池
- 延迟连接:engine 在首次 create_engine() 返回时并未连接到数据,它在首次要求对数据库执行任务时连接。
1 | from sqlalchemy import create_engine |
事务
- engine 的目的是通过提供 Connection 对象来连接数据库
1 | # text: 将SQL语句编写成文本SQL |
查
1 | with engine.connect() as conn: |
- 参数传入
1 | with engine.connect() as conn: |
1 | from sqlalchemy.orm import Session |
DBAPI
数据库元数据
- MetaData
- Table
- Column
1 | from sqlalchemy import MetaData |
1 | from sqlalchemy.orm import DeclarativeBase |
- 表反射:通过读取数据的当前状态来生成Table和相关对象的过程
1 | some_table = Table("some_table", metadata_obj, autoload_with=engine) |
insert
1 | from sqlalchemy import insert |
- insert语句可以带returning
1 | select_stmt = select(user_table.c.id, user_table.c.name + "@aol.com") |
select
- 生成一个Select构造
- 生成式方法,每个方法都在对象上构造更多状态
1 | from sqlalchemy import select |
where
1 | print(user_table.c.name == "squidward") |
对于针对单个实体的简单“相等”比较,还有一种流行的方法称为 Select.filter_by(),它接受与列键或 ORM 属性名称匹配的关键字参数。它将根据最左侧的 FROM 子句或最后加入的实体进行过滤
1 | print(select(User).filter_by(name="spongebob", fullname="Spongebob Squarepants")) |
from-join
1 | print(select(user_table.c.name)) |
order by-group by-having
1 | # 排序 |
子查询
1 | # SQLAlchemy 使用 Subquery 对象来表示子查询,使用 CTE 来表示 CTE,通常分别从 Select.subquery() 和 Select.cte() 方法获得。 这两个对象都可以用作较大 select() 构造中的 FROM 元素。 |
CTE 构造还具有以“递归”样式使用的能力,并且在更复杂的情况下,可以从 INSERT、UPDATE 或 DELETE 语句的 RETURNING 子句中组成。 CTE 的文档字符串包含有关这些其他模式的详细信息。
在这两种情况下,子查询和 CTE 都在 SQL 级别使用“匿名”名称命名。 在 Python 代码中,我们根本不需要提供这些名称。 Subquery 或 CTE 实例的对象标识充当呈现时对象的语法标识。 可以通过将其作为 Select.subquery() 或 Select.cte() 方法的第一个参数传递来提供将在 SQL 中呈现的名称。
1 | subq = select(Address).where(~Address.email_address.like("%@aol.com")).subquery() |
标量子查询
标量子查询:返回零行或一行且仅返回一列的子查询
SQLAlchemy 使用 ScalarSelect 构造表示标量子查询,它是 ColumnElement 表达式层次结构的一部分,而常规子查询由 Subquery 构造表示,它在 FromClause 层次结构中。
标量子查询通常(但不一定)与聚合函数一起使用,聚合函数在之前的 带有 GROUP BY / HAVING 的聚合函数 中介绍过。 标量子查询通过使用 Select.scalar_subquery() 方法显式指示,如下所示。 当自身进行字符串化时,它的默认字符串形式呈现为从两个表选择的普通 SELECT 语句
1 | subq = ( |
集合操作
1 | union() |
exists子查询
1 | subq = ( |
sql函数
1 | print(select(func.count()).select_from(user_table)) |
返回类型
1 | # pre-configured SQL function (only a few dozen of these) |
update
1 | from sqlalchemy import update |
delete
1 | from sqlalchemy import delete |
peewee
peewee:轻量 python orm 库,易于学习,直观使用。
- Python 中是最流行的 ORM 框架之一,以简单为卖点
- 同时兼容多种数据库系统:如 sqlite, mysql、postgresql
- sqlite 扩展:包括 sqlite 日期操作函数的 cython 实现、regexp 运算符和全文搜索结果排名算法
- pymysql:mysql
- psycopg2:postgres
- 功能:
- model definition
- storing data
- retrieving data 数据检索
- 参考资料:
基本概念
- Model class 类 = 数据库的表
- Field instance 实例 = Column 表的列
- Model instance 实例 = Row 表的行
基本使用
1 | # 直接连接 |
1 | # SQL |
连接状态
1 | python |
字段类型
- 避免字段命名冲突,如 命名为
create - 查看表的所有列名:
_._meta.fields.keys()
| Field Type | Sqlite | Postgresql | MySQL |
|---|---|---|---|
AutoField | integer | serial | integer |
BigAutoField | integer | bigserial | bigint |
IntegerField | integer | integer | integer |
BigIntegerField | integer | bigint | bigint |
SmallIntegerField | integer | smallint | smallint |
IdentityField | not supported | int identity | not supported |
FloatField | real | real | real |
DoubleField | real | double precision | double precision |
DecimalField | decimal | numeric | numeric |
CharField | varchar | varchar | varchar |
FixedCharField | char | char | char |
TextField | text | text | text |
BlobField | blob | bytea | blob |
BitField | integer | bigint | bigint |
BigBitField | blob | bytea | blob |
UUIDField | text | uuid | varchar(40) |
BinaryUUIDField | blob | bytea | varbinary(16) |
DateTimeField | datetime | timestamp | datetime |
DateField | date | date | date |
TimeField | time | time | time |
TimestampField | integer | integer | integer |
IPField | integer | bigint | bigint |
BooleanField | integer | boolean | bool |
BareField | untyped | not supported | not supported |
ForeignKeyField | integer | integer | integer |
| 字段类型 | 特殊参数 |
|---|---|
CharField | max_length,最大长度,默认为 255 |
FixedCharField | max_length |
DateTimeField | formats,'%Y-%m-%d %H:%M:%S' 具有属性 year, month, day, hour, minute, second |
DateField | formats,具有属性 year, month, day |
TimeField | formats,具有属性 hour, minute, second |
TimestampField | resolution, utc |
DecimalField | max_digits=15 最大位数, decimal_places=2 最大精度, auto_round=True 自动舍入值, rounding |
ForeignKeyField | |
BareField | adapt |
1 | # 参数与默认值 |
常用字段类型的特殊参数
1 | CharField、FixedCharField: |
1 | DecimalField: |
1 | ForeignKeyField: |
主键索引约束
| 期权 | 意义 | Inheritable? |
|---|---|---|
database | 模型数据库 | 对 |
table_name | 要存储数据的表的名称 | 不 |
table_function | 动态生成表名的函数 | 对 |
indexes | 要索引的字段列表 | 对 |
primary_key | 一 CompositeKey 实例 | 对 |
constraints | 表约束列表 | 对 |
schema | 模型的数据库架构 | 对 |
only_save_dirty | 调用 model.save()时,只保存脏字段 | 对 |
options | 用于创建表扩展名的选项字典 | 对 |
table_settings | 将字符串设置在右括号后的列表 | 对 |
temporary | 表示临时表 | 对 |
legacy_table_names | 使用旧表名生成(默认情况下启用) | 对 |
depends_on | 指示此表依赖另一个表进行创建 | 不 |
without_rowid | 指示表不应具有 rowid(仅限于 sqlite) | 不 |
1 | # 主键 |
增 -insert
1 | # Model.save() |
删
1 | # 删, delete: Model.delete_instance() 和 Model.delete() |
改
1 | # Model.save() |
查
1 | # Model.get() |
- 请注意,peewee 使用 bitwise 操作员(
&和|)而不是逻辑运算符(and和or)原因是 python 将逻辑操作的返回值强制为布尔值。这也是必须使用.in_()而不是in操作员。- 使用
.in_()和.not_in()而不是in和not in - 使用
&而不是and - 使用
|而不是or - 使用
~而不是not - 使用
.is_null()而不是is None或== None. - 使用逻辑运算符时,不要忘记将比较结果括在括号中。
- 使用
1 | # where |
| 比较 | 意义 |
|---|---|
== | x 等于 y |
< | x 小于 y |
<= | x 小于或等于 y |
> | x 大于 y |
>= | x 大于或等于 y |
!= | x 不等于 y |
<< | x 代表 y,其中 y 代表列表或查询 |
>> | x 是 y,其中 y 是无/空 |
% | x 类似 y,其中 y 可能包含通配符 |
** | x i 类似 y,其中 y 可能包含通配符 |
^ | X 异或 Y |
~ | 一元否定(例如,不是 x) |
| 方法 | 意义 | ||
|---|---|---|---|
.in_(value) | 在查找中(与 << ) | ||
.not_in(value) | 不在查找中。 | ||
.is_null(is_null) | 为空或不为空。接受布尔参数。 | ||
.contains(substr) | 子字符串的通配符搜索。 | ||
.startswith(prefix) | 搜索以开头的值 prefix . | ||
.endswith(suffix) | 搜索以结尾的值 suffix . | ||
.between(low, high) | 搜索介于 low 和 high . | ||
.regexp(exp) | 正则表达式匹配(区分大小写)。 | ||
.iregexp(exp) | 正则表达式匹配(不区分大小写)。 | ||
.bin_and(value) | 二进制和 | ||
.bin_or(value) | 二进制或 | ||
.concat(other) | 使用连接两个字符串或对象 ` | ` . | |
.distinct() | 为非重复选择标记列。 | ||
.collate(collation) | 使用给定的排序规则指定列。 | ||
.cast(type) | 将列的值强制转换为给定的类型。 |
高级与常用
model_to_dict:将查询到的结果转为 dict,backrefs 是一个千金这项,表示返回关联表的数据
json_serial:转换 datetime 为 json 格式的方法,可以避免包含 datetime 时转换出错
遇到的问题
- 问题:peewee.ImproperlyConfigured: MySQL driver not installed!
- 解决方案:
1 | pip install pymysql |
dataset
dataset:提供了一个简单的抽象层, 无需完整的 ORM 可省大量 SQL 语句,可像 JSON 和 NoSQL 一样操作数据库
1 | import dataset |
- 多线程连接 sqlite3 时,报:SQLite objects created in a thread can only be used in that same thread:
1 | # 连接配置中加入; |
Gadfly
Gadfly:提供了一个完整的 SQL 环境
sandman2
sandman2:号称不用一行代码,用 API 操作数据库,不咋地,OUT
- 基于已存在的数据库,生成一个 RESTful API 服务器
- python3.8+Windows 上有个小 BUG,添加数据时报
If this is an auto-generated value, check that the database table allows generation of new primary key values...,界面不美观,OUT
