数据并发访问:想让几个用户同时对基于磁盘的数据进行读写而不造成任何文件损坏。或者同时使用多个数据字段或属性进行复杂的搜索。
数据库——数据量巨大而同时还希望其他程序员容易理解。
本章会对Python的数据库编程接口进行讨论,这是一种连接SQL数据库的方法;同时也会展示如何通过API执行一些基本的SQL命令。
Python数据库编程接口(API)
在提供相同功能的不同模块之间进行切换时的问题通常是它们的接口(API)不同。为了解决Python中各种数据库模块间的兼容问题,通过一个标准的数据库编程接口(DB API)。
全局变量
如果想让程序同时应用于几个数据库,比较现实的做法是检查这些变量,看看给定的数据库模块是否能被程序接受。如果不能,就显示合适的错误信息然后退出。
变量名 | 用途 |
---|---|
apilevel | 所使用的的Python DB API版本 |
threadsafety | 模块的线程安全等级 |
paramstyle | 在SQL查询中使用的参数风格 |
API级别(apilevel)是个字符串常量,提供正在使用的API版本号。
线程安全性等级(trheadsafety)是个取值范围为0~3的整数。0表示线程完全不共享模块,而3表示模块是完全线程安全的。1表示线程本身可以共享模块,但不对连接共享。不使用多线程,完全不用担心这个变量。
参数风格(paramstyle)表示在执行多次类似查询的时候,参数是如何被拼接到SQL查询中的。
异常
API中定义了一些异常类,以便尽可能进行错误处理。
异常 | 超类 | 描述 |
---|---|---|
StandardError | 所有异常的泛型基类 | |
Warning | StandardError | 非致命错误发生时引发 |
Error | StandardError | 所有错误条件的泛型超类 |
InterfaceError | Error | 关于接口而非数据库的错误 |
DatabaseError | Error | 与数据库相关的错误的基类 |
DataError | DatabaseError | 与数据库相关的问题,比如值超出范围 |
OperationalError | DatabaseError | 数据库内部操作错误 |
IntegrityError | DatabaseError | 关系完整性收到影响,比如键检查失败 |
InternalError | DatabaseError | 数据库内部错误没比如非法游标 |
ProgrammingError | DatabaseError | 用户编程错误,比如未找到表 |
NotSupportedError | DatabaseError | 请求不支持的特性(比如回滚) |
连接和游标
为了使用底层的数据库系统,首先必须连接到它。
connect函数的常用参数
参数名 | 描述 | 是否可选 |
---|---|---|
dsn | 数据源名称,给出该参数表示数据库依赖 | 否 |
user | 用户名 | 是 |
password | 用户密码 | 是 |
host | 主机名 | 是 |
database | 数据库名 | 是 |
connect函数返回连接对象——表示目前的和数据库的会话。
连接对象方法
方法名 | 描述 |
---|---|
close() | 关闭连接之后,连接对象和它的游标均不可用 |
commit() | 如果支持的话就提交挂起的事务,否则不做任何事 |
rollback() | 回滚挂起的事务(可能不可用) |
cursor() | 返回连接的游标对象 |
rollback方法可能不可用,因为不是所有的数据库都支持事务(事务是一些列动作)。
游标对象——通过游标执行SQL查询并检查结果
类型
DB API 构造函数和特殊值
名称 | 描述 |
---|---|
Data(year, month, day) | 创建保存日期值的对象 |
Time(hour, minute, second) | 创建保存时间值得对象 |
Timestamp(y, mon, d, h, min, s) | 创建保存时间戳值得对象 |
DataFromTicks(ticks) | 创建保存自新纪元以来的秒数对象 |
TimeFromTicks(ticks) | 创建保存来自秒数的时间值的对象 |
TimestampFromTicks(ticks) | 创建保存来自秒数的时间戳值得对象 |
Binary(string) | 创建保存二进制字符串值的对象 |
STRING | 描述基于字符串的列类型(比如CHAR) |
BINARY | 描述二进制列(比如LONG或RAW) |
NUMBER | 描述数字列 |
DATETIME | 描述日期/时间列 |
ROWID | 描述行ID列 |
SQLite和PySQLite
可用的SQL数据库引擎有很多,且都有相应的python模块。
入门
可以将SQLite作为一个模块导入,模块名称为sqlite3(标准库中的模块)。之后就可以创建一个到数据库文件的连接——如果文件不存在就自动生成——通过提供一个文件名(可以是文件的绝对路径或者相对路径):
>>> import sqlite3
>>> conn = sqlite3.connect('somedatabase.db')
之后就能获得连接的游标:
>>> curs = conn.cursor()
这个游标可以用来执行SQL查询。完成查询并且做出某些更改后确保已经进行了提交,这样才可以将这些修改真正地保存到文件中:
>>> conn.commit()
可以(而且是应该)在每次修改数据库后都进行提交,而不是仅仅在准备关闭时才提交,准备关闭数据库时,使用close方法:
>>> conn.close()
数据库应用程序示例
创建和填充表
# importdata.py
import sqlite
def convert(value):
if value.startswith('~'):
return value.strip('~')
if not value:
value = '0'
return float(value)
conn = sqlite.connect('food.db')
curs = conn.cursor()
curs.execute('''
CREATE TABLE food (
id TEXT PRIMARY KEY,
desc TEXT,
water FLOAT,
kcal FLOAT,
protein FLOAT,
fat FLOAT,
ash FLOAT,
carbs FLOAT,
fiber FLOAT,
sugar FLOAT
)
''')
field_count = 10
markers = ', '.join(['%s']*field_count)
query = 'INSERT INTO food VALUES (?,?,?,?,?,?,?,?,?,?)'
for line in open('ADD_FOOD.txt'):
fields = line.split('^')
vals = [convert(f) for f in fields[:field_count]]
curs.execute(query, vals)
conn.commit()
conn.close()
搜索和处理结果
使用数据库——创建连接并且获得该连接的游标。使用execute方法执行SQL查询,用fetchall等方法提取结果。
# food_query.py
import sqlite, sys
conn = sqlite.connect('food.db')
curs = conn.cursor()
query = 'SELECT * FROM food WHERE %s' % sys.argv[1]
print query
curs.execute(query)
names = [f[0] for f in curs.description]
for row in curs.fetchall():
for pair in zip(names, row):
print '%s: %s' % pair
print
执行下面命令
$ python food_query.py "kcal <= 100 AND fiber >= 10 ORDER BY sugar"
小结
- Python数据库编程接口(DB API)
- 连接
- 游标
- 类型和特殊值
- SQLite