大约是一年半或者是两年前的样子,在那段时间里,我似乎特别痴迷于各种数据库以及相关的技术。其间看了不少书,可能是平常工作接触的多是
sqlserver
的缘故,我本人对其反而不是特别的感冒,所以看的多是关于
postgresql
和
oracle
的,尤其是前者。记得当时用的是一台
celeron1.7G
、内存
256MB
的方正电脑,安装这些软件让电脑变得非常的慢,终于在一次忍无可忍的僵死之后,我卸载了所有这些庞然大物。
之后台里引进了一台用于上下班记录的打卡机,为了方便统计迟到早退,我用
perl/tk
写了一个采集整理程序,后台用到了
sqlite
,这也是我和
sqlite
的初次邂逅。
至于对
python
,不好意思地说,我一直抱着骑墙的态度。如果
perl
可以解决的,我是断然不会用
python
的,这一切直到我对
poe
的幻想破灭为止。原先我是期待用
poe
来作为多线程的替代,可事实还是向着相反的方向发展。
sqlite
是一个嵌入式的数据库,也就是说没有独立的维护进程,所有的维护都来自于程序本身。麻雀虽小,五脏俱全,
sqlite
实现了多数
sql-92
的标准,比如说
transaction
、
trigger
和复杂的查询等。
有很多介绍的书籍,记得有一本
apress
出版的《
The difinitive guide to sqlite
》,有毛
500
面的样子,用来介绍一个嵌入式数据库,着实有点长了,当然对于初学者是有其存在的价值的。
而
pysqlite
则是一个
sqlite
为
python
提供的
api
接口,它让一切对于
sqlite
的操作都变得异常简单。
userguide
并不长,但是基本上都说到点子上了,不过这里还是要对其提一下几个要点。事实上我个人认为,理解了这几点就完全足够了。
“
from pysqlite2 import dbapi2 as sqlite
”,
这就是一切的起源。
下面是一个涉及到我即将介绍的几点的示例程序,内容很简单,就是将一个
Point
对象存入
sqlite
建立的内存数据库中,然后原封不动地返回出来。
from pysqlite2 import dbapi2 as sqlite
class Point(object):
def __init__(self, x, y):
self.x, self.y = x, y
def __conform__(self, protocol):
if protocol is sqlite.PrepareProtocol:
return "%d;%d" % (self.x, self.y)
def adapt_converter(point):
return Point(*[int(x) for x in point.split(";")])
sqlite.register_converter('point', adapt_converter)
con=sqlite.connect(":memory:",detect_types= sqlite.PARSE_COLNAMES)
cur = con.cursor()
cur.execute('select ? as "a [point]"', (Point(2, 3), ))
print type(cur.fetchone()[0])
首先要做一些准备工作,因为
sqlite
不是什么
python
对象都能够接受的
,
为此需要一些必要的转换。这个例子里,
sqlite
就像是一个汽车维修站。在站里,为了维修,汽车被拆成了个个零件,等出站时又被组装成了一辆汽车。
第一步是声明一个
Point
类。这个类除了初始化函数
__init__
之外,还有一个两参量的函数
__conform__
,
sqlite
在检查存入数据库对象的时候会检查是否存在该函数,如果存在,则将该函数返回的值替代对象本身存入数据库。可以说这是一个转换器,将
python
对象转换为
sqlite
允许接纳对象,即
Null
、
Integer
、
Real
、
Text
和
Blob
其中之一。在这里,显然
sqlite
是无法接纳
Point
这个类所建立的
Point(2,3)
对象的,所以通过
__conform__
,实际将存入内容为“
2;3
”的文本。
使用“
sqlite.register_adapter(Point, lambda p = point : “%d;%d” % p.x, p.y)
”,在
sqlite
中注册一个适配器也可以完成转换的功能,但是我个人认为这样比较麻烦,不如写在类里来得清楚。
在处理完了
Point
类之后,为了使文本“
2;3
”在从数据库取出时能转换为
Point
对象,还需要用“
sqlite.register_converter
”注册一个叫“
adapt_converter
”的转换器。
这样一条拆卸组装的流水线就完成了。
实际使用到的只有两样东西,一是数据库连接对象
con
,还有一个就是用来执行命令并返回结果(有必要的话)的游标对象
cur
,它是由
con
对象产生的。
为了使
sqlite
意识到它正在处理的表中的某一列是已注册需要转换的“
phone
”,我们需要在建立表的时候声明该列的属性,或者在查询的时候显示地表明该列的属性。对于前者,建立
con
对象是应将
detect_types
设置为
sqlite.PARSE_DELTYPES
;后者,则是设置为
sqlite.PARSE_COLNAMES
。这里,因为使用的是一个临时表,所以只能使用后者的设置办法。
事实上,“
select ? as ……
”这句包含了两个方面的内容,首先它将
Point(2, 3)
存入了临时表中,这涉及到了
Phone
对象到文本的转换。之后,再将文本数据从临时表中取出,又用到了文本到
Phone
的反转换。
下面是一种浅显而罗嗦的写法:
cur.execute("create table temp(t)")
cur.execute("insert into temp values (?)", (Point(2, 3),))
cur.execute('select ? as "a [point]" from temp')
最后,打印从
sqlite
中取出的对象类型,内容为“
<class '__main__.Point'>
”。
以上这些,可以总结为两点:有必要的话,做好转换;建立
con
和
cur
。理解了这些,差不多就能用
pysqlite
了。当然如果要做工程、写程序还需要很多的其他知识,至少数据库设计这方面的内容是要看一下的。